diff --git a/.gitignore b/.gitignore index ef8c9bdf..9865b6bd 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ build/ *.setting bin/ *.iml +.idea/* +/out diff --git a/CHANGES b/CHANGES index cceda86d..3bacd672 100644 --- a/CHANGES +++ b/CHANGES @@ -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 '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 +-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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..16b68a09 --- /dev/null +++ b/CONTRIBUTING.md @@ -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. diff --git a/README.md b/README.md index 63c0e904..1e1ce33b 100644 --- a/README.md +++ b/README.md @@ -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/) diff --git a/brut.apktool.smali/baksmali/.gitignore b/brut.apktool.smali/baksmali/.gitignore deleted file mode 100644 index ea8c4bf7..00000000 --- a/brut.apktool.smali/baksmali/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/brut.apktool.smali/baksmali/build.gradle b/brut.apktool.smali/baksmali/build.gradle index a23cada3..9686504e 100644 --- a/brut.apktool.smali/baksmali/build.gradle +++ b/brut.apktool.smali/baksmali/build.gradle @@ -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") } -} \ No newline at end of file + + 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) diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/AnnotationFormatter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/AnnotationFormatter.java index edb4fcee..2b9614f1 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/AnnotationFormatter.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/AnnotationFormatter.java @@ -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 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"); } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java index 2c5541f4..6c67d4ac 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CatchMethodItem.java @@ -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); diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java index dd6b27f0..a4eb6913 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java @@ -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 fieldsSetInStaticConstructor; + @Nonnull public final baksmaliOptions options; + @Nonnull public final ClassDef classDef; + @Nonnull private final HashSet 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(); + @Nonnull + private HashSet findFieldsSetInStaticConstructor() { + HashSet fieldsSetInStaticConstructor = new HashSet(); - if (classDataItem == null) { - return; - } - - for (ClassDataItem.EncodedMethod directMethod: classDataItem.getDirectMethods()) { - if (directMethod.method.getMethodName().getStringValue().equals("") && - 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("")) { + 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 staticFields = writeStaticFields(writer); + writeInstanceFields(writer, staticFields); + Set 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 interfaces = Lists.newArrayList(classDef.getInterfaces()); + Collections.sort(interfaces); - List 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 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 writeStaticFields(IndentingWriter writer) throws IOException { + boolean wroteHeader = false; + Set writtenFields = new HashSet(); - EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers(); - - EncodedValue[] staticInitializers; - if (encodedStaticInitializers != null) { - staticInitializers = encodedStaticInitializers.getEncodedArray().values; + Iterable staticFields; + if (classDef instanceof DexBackedClassDef) { + staticFields = ((DexBackedClassDef)classDef).getStaticFields(false); } else { - staticInitializers = new EncodedValue[0]; + staticFields = classDef.getStaticFields(); } - List 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 staticFields) throws IOException { + boolean wroteHeader = false; + Set writtenFields = new HashSet(); + + Iterable 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 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 writeDirectMethods(IndentingWriter writer) throws IOException { + boolean wroteHeader = false; + Set writtenMethods = new HashSet(); + + Iterable directMethods; + if (classDef instanceof DexBackedClassDef) { + directMethods = ((DexBackedClassDef)classDef).getDirectMethods(false); + } else { + directMethods = classDef.getDirectMethods(); } - List encodedFields = classDataItem.getInstanceFields(); - if (encodedFields.size() == 0) { - return; - } - - writer.write("\n\n"); - writer.write("# instance fields\n"); - for (int i=0; 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 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 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 methods) throws IOException { - for (int i=0; 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 directMethods) throws IOException { + boolean wroteHeader = false; + Set writtenMethods = new HashSet(); + + Iterable 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); } } } diff --git a/brut.apktool.smali/util/src/main/java/org/jf/util/CommentingIndentingWriter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentingIndentingWriter.java similarity index 85% rename from brut.apktool.smali/util/src/main/java/org/jf/util/CommentingIndentingWriter.java rename to brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentingIndentingWriter.java index 9b1de4f3..c63da1cd 100644 --- a/brut.apktool.smali/util/src/main/java/org/jf/util/CommentingIndentingWriter.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/CommentingIndentingWriter.java @@ -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(); } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/BeginEpilogueMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/BeginEpilogueMethodItem.java new file mode 100644 index 00000000..a1294fcb --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/BeginEpilogueMethodItem.java @@ -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; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/DebugMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/DebugMethodItem.java new file mode 100644 index 00000000..86d30f97 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/DebugMethodItem.java @@ -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()); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/EndLocalMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/EndLocalMethodItem.java new file mode 100644 index 00000000..231e0498 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/EndLocalMethodItem.java @@ -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; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/EndPrologueMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/EndPrologueMethodItem.java new file mode 100644 index 00000000..369c38fd --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/EndPrologueMethodItem.java @@ -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; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LineNumberMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LineNumberMethodItem.java new file mode 100644 index 00000000..91473bdc --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LineNumberMethodItem.java @@ -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; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LocalFormatter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LocalFormatter.java new file mode 100644 index 00000000..62ed9958 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/LocalFormatter.java @@ -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); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/RestartLocalMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/RestartLocalMethodItem.java new file mode 100644 index 00000000..44617193 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/RestartLocalMethodItem.java @@ -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; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/SetSourceFileMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/SetSourceFileMethodItem.java new file mode 100644 index 00000000..faccfdf3 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/SetSourceFileMethodItem.java @@ -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; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/StartLocalMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/StartLocalMethodItem.java new file mode 100644 index 00000000..0cd2d2b1 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Debug/StartLocalMethodItem.java @@ -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; + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/DebugMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/DebugMethodItem.java deleted file mode 100644 index 858c5251..00000000 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/DebugMethodItem.java +++ /dev/null @@ -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('"'); - } -} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/EncodedValue/AnnotationEncodedValueAdaptor.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/EncodedValue/AnnotationEncodedValueAdaptor.java index dd514d97..66cd506c 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/EncodedValue/AnnotationEncodedValueAdaptor.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/EncodedValue/AnnotationEncodedValueAdaptor.java @@ -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 annotationElements) throws IOException { writer.indent(4); - for (int i=0; i { - public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, ArrayDataPseudoInstruction instruction) { - super(codeItem, codeAddress, instruction); +public class ArrayDataMethodItem extends InstructionMethodItem { + 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 iterator = instruction.getElements(); - while (iterator.hasNext()) { - ArrayDataPseudoInstruction.ArrayElement element = iterator.next(); - for (int i=0; i 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"); diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java index 2b1e42e3..300efd81 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java @@ -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 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 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 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 extends MethodItem { writeFirstRegister(writer); writer.write(", "); writeTargetLabel(writer); - return true; + break; case Format22b: case Format22s: writeOpcode(writer); @@ -140,17 +242,16 @@ public class InstructionMethodItem 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 extends MethodItem { writeSecondRegister(writer); writer.write(", "); writeFieldOffset(writer); - return true; + break; case Format22t: writeOpcode(writer); writer.write(' '); @@ -168,7 +269,7 @@ public class InstructionMethodItem extends MethodItem { writeSecondRegister(writer); writer.write(", "); writeTargetLabel(writer); - return true; + break; case Format22x: case Format32x: writeOpcode(writer); @@ -176,7 +277,7 @@ public class InstructionMethodItem extends MethodItem { writeFirstRegister(writer); writer.write(", "); writeSecondRegister(writer); - return true; + break; case Format23x: writeOpcode(writer); writer.write(' '); @@ -185,71 +286,77 @@ public class InstructionMethodItem 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 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 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 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 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()); } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItemFactory.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItemFactory.java index 772d7311..429cb692 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItemFactory.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItemFactory.java @@ -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(codeItem, codeAddress, instruction); + return new InstructionMethodItem(methodDef, codeAddress, instruction); } } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/OffsetInstructionFormatMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/OffsetInstructionFormatMethodItem.java index d4edf3d0..3ffb4bd4 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/OffsetInstructionFormatMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/OffsetInstructionFormatMethodItem.java @@ -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 { 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 { +public class PackedSwitchMethodItem extends InstructionMethodItem { private final List 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(); - Iterator 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= 0) { + if (target >= 0) { writer.write('+'); } - writer.printSignedIntAsDec(Target); + writer.printSignedIntAsDec(target); } } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java index 46f49dc2..0dee84ab 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java @@ -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 { +public class SparseSwitchMethodItem extends InstructionMethodItem { private final List 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(); - Iterator 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 "); target.writeTargetTo(writer); + writeResourceId(writer, target.getKey()); writer.write('\n'); } writer.deindent(4); @@ -92,24 +80,38 @@ public class SparseSwitchMethodItem extends InstructionMethodItem= 0) { + if (target >= 0) { writer.write('+'); } - writer.printSignedIntAsDec(Target); + writer.printSignedIntAsDec(target); } } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java index 5451a203..a7768817 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/UnresolvedOdexInstructionMethodItem.java @@ -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 { - 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 instructions; + @Nonnull public final ImmutableList 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 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 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 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 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 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 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 getMethodItems() { ArrayList methodItems = new ArrayList(); - 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 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 methodItems) { int currentCodeAddress = 0; - for (int i=0; i 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 instructions = methodAnalyzer.getInstructions(); + List instructions = methodAnalyzer.getAnalyzedInstructions(); int currentCodeAddress = 0; for (int i=0; i methodItems) { - if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) { + List> 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 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 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; + } + } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PostInstructionRegisterInfoMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PostInstructionRegisterInfoMethodItem.java index d4179603..812a282a 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PostInstructionRegisterInfoMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PostInstructionRegisterInfoMethodItem.java @@ -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= 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; diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java index bdbfea32..d2811c04 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java @@ -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= 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; } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java index 7a0ec5a1..91d142a8 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/ReferenceFormatter.java @@ -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()); - } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/RegisterFormatter.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/RegisterFormatter.java index 35c620d5..bffcb385 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/RegisterFormatter.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/RegisterFormatter.java @@ -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 format for both. If only the last register is a parameter register, it will * use the v format for both, otherwise it would be confusing to have something like {v20 .. p1} * @param writer the IndentingWriter to write to - * @param codeItem the CodeItem 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 format instead. * * @param writer the IndentingWriter to write to - * @param codeItem the CodeItem 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))); diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java index 1a30e08e..ef0abb7a 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java @@ -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; } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/CharRenderer.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/CharRenderer.java index 90854d15..daf76340 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/CharRenderer.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/Renderers/CharRenderer.java @@ -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('\''); } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java index db45281e..fcc89d7a 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -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 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 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 classDefItems = new ArrayList(dexFile.ClassDefsSection.getItems()); - Collections.sort(classDefItems, new Comparator() { - public int compare(ClassDefItem classDefItem1, ClassDefItem classDefItem2) { - return classDefItem1.getClassType().getTypeDescriptor().compareTo(classDefItem1.getClassType().getTypeDescriptor()); - } - }); + List 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> 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() { + @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 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; } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java new file mode 100644 index 00000000..07a05869 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java @@ -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 bootClassPathDirs = Lists.newArrayList(); + + public List bootClassPathEntries = Lists.newArrayList(); + public List extraClassPathEntries = Lists.newArrayList(); + + public Map resourceIdFileEntries = new HashMap(); + public Map resourceIds = new HashMap(); + + 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]); + } + } +} diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/dump.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/dump.java index f428642e..bd040e6d 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/dump.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/dump.java @@ -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); } } } diff --git a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java index 87d6a1bf..a8b1e241 100644 --- a/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java +++ b/brut.apktool.smali/baksmali/src/main/java/org/jf/baksmali/main.java @@ -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 bootClassPathDirs = new ArrayList(); - 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= 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.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 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"); + } + } } \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/main/resources/baksmali.properties b/brut.apktool.smali/baksmali/src/main/resources/baksmali.properties new file mode 100644 index 00000000..df22408c --- /dev/null +++ b/brut.apktool.smali/baksmali/src/main/resources/baksmali.properties @@ -0,0 +1 @@ +application.version=${version} \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/main/resources/properties/baksmali.properties b/brut.apktool.smali/baksmali/src/main/resources/properties/baksmali.properties deleted file mode 100644 index 0ae2c8bb..00000000 --- a/brut.apktool.smali/baksmali/src/main/resources/properties/baksmali.properties +++ /dev/null @@ -1 +0,0 @@ -application.version=1.4.1 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/main/resources/templates/templates/baksmali.stg b/brut.apktool.smali/baksmali/src/main/resources/templates/templates/baksmali.stg deleted file mode 100644 index 8b5b42f7..00000000 --- a/brut.apktool.smali/baksmali/src/main/resources/templates/templates/baksmali.stg +++ /dev/null @@ -1,436 +0,0 @@ -group baksmali; - -smaliFile(AccessFlags, ClassType, SuperType, SourceFile, Interfaces, Annotations, StaticFields, - InstanceFields, DirectMethods, VirtualMethods) ::= -<< -.class }> - -.super - - - -.source "" - - - - - -# interfaces - - - - - - -# annotations - - - - - - -# static fields - - - - - - -# instance fields - - - - - - -# direct methods - - - - - - -# virtual methods - - - ->> - - - - -implement(interface) ::= -<< -.implements ->> - - -annotation(Visibility, AnnotationType, Elements) ::= -<< -.annotation - - - - - - - -.end annotation ->> - - - -field(AccessFlags, FieldName, FieldType, Annotations, InitialValue, Comments) ::= -<< -} ; separator="\n"> - - -.field }>: = - - -.end field - - ->> - - -method(AccessFlags, MethodName, Prototype, HasCode, RegistersDirective, RegisterCount, Parameters, Annotations, - MethodItems) ::= -<< -.method }> - - - - - - - - - - - - - -.end method ->> - -Parameter(ParameterName, Annotations) ::= -<< -.parameter "" - - -.end parameter - ->> - -Format10t(Opcode, TargetLabel) ::= -<< - ->> - -Format10x(Opcode) ::= -<< - ->> - -Format11n(Opcode, RegisterA, Literal) ::= -<< - , ->> - -Format11x(Opcode, RegisterA) ::= -<< - ->> - -Format12x(Opcode, RegisterA, RegisterB) ::= -<< - , ->> - -Format20t(Opcode, TargetLabel) ::= -<< - ->> - -Format21c(Opcode, RegisterA, Reference) ::= -<< - , ->> - -Format21h(Opcode, RegisterA, Literal) ::= -<< - , ->> - -Format21s(Opcode, RegisterA, Literal) ::= -<< - , ->> - -Format21t(Opcode, RegisterA, TargetLabel) ::= -<< - , ->> - -Format22b(Opcode, RegisterA, RegisterB, Literal) ::= -<< - , , ->> - -Format22c(Opcode, RegisterA, RegisterB, Reference) ::= -<< - , , ->> - -Format22cs(Opcode, RegisterA, RegisterB, FieldOffset) ::= -<< - , , field@ ->> - -Format22s(Opcode, RegisterA, RegisterB, Literal) ::= -<< - , , ->> - -Format22t(Opcode, RegisterA, RegisterB, TargetLabel) ::= -<< - , , ->> - -Format22x(Opcode, RegisterA, RegisterB) ::= -<< - , ->> - -Format23x(Opcode, RegisterA, RegisterB, RegisterC) ::= -<< - , , ->> - -Format30t(Opcode, TargetLabel) ::= -<< - ->> - -Format31c(Opcode, RegisterA, Reference) ::= -<< - , ->> - -Format31i(Opcode, RegisterA, Literal) ::= -<< - , ->> - -Format31t(Opcode, RegisterA, TargetLabel) ::= -<< - , ->> - -Format32x(Opcode, RegisterA, RegisterB) ::= -<< - , ->> - -Format35c(Opcode, Registers, Reference) ::= -<< - {}, ->> - -Format35s(Opcode, Registers, Reference) ::= -<< - {}, ->> - -Format35ms(Opcode, Registers, MethodIndex) ::= -<< - {}, vtable@ ->> - -Format3rc(Opcode, StartRegister, LastRegister, Reference) ::= -<< - { .. }, ->> - -Format3rms(Opcode, StartRegister, LastRegister, MethodIndex) ::= -<< - { .. }, vtable@ ->> - -Format51l(Opcode, RegisterA, Literal) ::= -<< - , ->> - -CommentedOutMethodItem(MethodItem) ::= -<< -# ->> - -UnresolvedNullReference(Opcode, Register, UseInvokeRange, AddGoto) ::= -<< - -#Replaced unresolvable optimized invoke-*-range-quick instruction -#with a generic method call that will throw a NullPointerException -invoke-virtual/range { .. }, Ljava/lang/Object;->hashCode()I -goto/32 0 - -#Replaced unresolvable optimized instruction with a throw -throw - ->> - - -ArrayData(Opcode, ElementWidth, Values, Dead) ::= -<< -#.array-data - -}; separator="\n"> - -}; separator="\n"> - - -#.end array-data ->> - -ArrayElement(Bytes) ::= -<< - ->> - -PackedSwitchData(Opcode, FirstKey, Targets, Dead) ::= -<< -#.packed-switch - -}; separator="\n"> - -}; separator="\n"> - - -#.end packed-switch ->> - -SparseSwitchData(Opcode, Targets, Dead) ::= -<< -#.sparse-switch - - -> }; separator="\n"> - - -> }; separator="\n"> - - -#.end sparse-switch ->> - - -Label(Prefix, Suffix) ::= -<< -: ->> - -Line(Line) ::= -<< -.line ->> - -EndPrologue(Prologue) ::= -<< -.prologue ->> - -StartEpilogue(Epilogue) ::= -<< -.epilogue ->> - -StartLocal(Register, Name, Type, Signature) ::= -<< -.local , :,"" ->> - -EndLocal(Register, Name, Type, Signature) ::= -<< -.end local #:,, "" ->> - -RestartLocal(Register, Name, Type, Signature) ::= -<< -.restart local #:,, "" ->> - -SetFile(FileName) ::= -<< -.source "" ->> - -Blank(Blank) ::= -<< - ->> - -Catch(ExceptionType, StartLabel, EndLabel, HandlerLabel) ::= -<< -.catch .catchall { .. } ->> - - -StringReference(EscapedValue) ::= -<< -"" ->> - -FieldReference(ContainingClass, FieldName, FieldType) ::= -<< -->: ->> - -MethodReference(ContainingClass, MethodName, Prototype) ::= -<< --> ->> - -TypeReference(TypeDescriptor) ::= -<< - ->> - - -SimpleEncodedValue(Value) ::= -<< - ->> - -EncodedIndexedItemReference(Value) ::= -<< - ->> - -ArrayEncodedValue(Value) ::= -<< -{ - -} ->> - -EnumEncodedValue(Value) ::= -<< -.enum ->> - -AnnotationEncodedValue(AnnotationType, Elements) ::= -<< -.subannotation - -.end subannotation ->> - -AnnotationElement(Name, Value) ::= -<< - = ->> - -Comment(Comment) ::= -<< -# ->> \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java b/brut.apktool.smali/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java new file mode 100644 index 00000000..38219491 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java @@ -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); + } +} diff --git a/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/ConstructorTest.smali b/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/ConstructorTest.smali new file mode 100644 index 00000000..88e2eb86 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/ConstructorTest.smali @@ -0,0 +1,16 @@ +.class public LConstructorTest; +.super Ljava/lang/Object; + + +# direct methods +.method public constructor ()V + .registers 4 + + #v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LConstructorTest;); + invoke-direct {p0}, Ljava/lang/Object;->()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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/ConstructorTest2.smali b/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/ConstructorTest2.smali new file mode 100644 index 00000000..a376b25b --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/ConstructorTest2.smali @@ -0,0 +1,25 @@ +.class public LConstructorTest2; +.super Ljava/lang/Object; + + +# direct methods +.method public constructor ()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;->()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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/classes.dex b/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/classes.dex new file mode 100644 index 00000000..ef6e6d93 Binary files /dev/null and b/brut.apktool.smali/baksmali/src/test/resources/ConstructorTest/classes.dex differ diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateDirectMethods.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateDirectMethods.smali new file mode 100644 index 00000000..b1f30c9a --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateDirectMethods.smali @@ -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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateDirectVirtualMethods.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateDirectVirtualMethods.smali new file mode 100644 index 00000000..8d87c1d8 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateDirectVirtualMethods.smali @@ -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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateInstanceFields.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateInstanceFields.smali new file mode 100644 index 00000000..6efe9cf5 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateInstanceFields.smali @@ -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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateStaticFields.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateStaticFields.smali new file mode 100644 index 00000000..b71fbdff --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateStaticFields.smali @@ -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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateStaticInstanceFields.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateStaticInstanceFields.smali new file mode 100644 index 00000000..9a066b8d --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateStaticInstanceFields.smali @@ -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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateVirtualMethods.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateVirtualMethods.smali new file mode 100644 index 00000000..74af4c58 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/DuplicateVirtualMethods.smali @@ -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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/classes.dex b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/classes.dex new file mode 100644 index 00000000..68769440 Binary files /dev/null and b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/classes.dex differ diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateDirectMethods.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateDirectMethods.smali new file mode 100644 index 00000000..efb7abbb --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateDirectMethods.smali @@ -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 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateDirectVirtualMethods.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateDirectVirtualMethods.smali new file mode 100644 index 00000000..09d97b55 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateDirectVirtualMethods.smali @@ -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 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateInstanceFields.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateInstanceFields.smali new file mode 100644 index 00000000..1b92cd71 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateInstanceFields.smali @@ -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 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateStaticFields.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateStaticFields.smali new file mode 100644 index 00000000..3c01ba93 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateStaticFields.smali @@ -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 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateStaticInstanceFields.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateStaticInstanceFields.smali new file mode 100644 index 00000000..30a1fe6c --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateStaticInstanceFields.smali @@ -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 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateVirtualMethods.smali b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateVirtualMethods.smali new file mode 100644 index 00000000..3a6368eb --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/DuplicateVirtualMethods.smali @@ -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 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/README b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/README new file mode 100644 index 00000000..fae5c5a5 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/DuplicateTest/src/README @@ -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 \ No newline at end of file diff --git a/brut.apktool.smali/baksmali/src/test/resources/LocalTest/LocalTest.smali b/brut.apktool.smali/baksmali/src/test/resources/LocalTest/LocalTest.smali new file mode 100644 index 00000000..fe6d1adf --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/LocalTest/LocalTest.smali @@ -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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/LocalTest/classes.dex b/brut.apktool.smali/baksmali/src/test/resources/LocalTest/classes.dex new file mode 100644 index 00000000..5b6f026e Binary files /dev/null and b/brut.apktool.smali/baksmali/src/test/resources/LocalTest/classes.dex differ diff --git a/brut.apktool.smali/baksmali/src/test/resources/MultipleStartInstructionsTest/MultipleStartInstructionsTest.smali b/brut.apktool.smali/baksmali/src/test/resources/MultipleStartInstructionsTest/MultipleStartInstructionsTest.smali new file mode 100644 index 00000000..e329e6c4 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/MultipleStartInstructionsTest/MultipleStartInstructionsTest.smali @@ -0,0 +1,46 @@ +.class public LMultipleStartInstructionsTest; +.super Ljava/lang/Object; + + +# direct methods +.method public constructor (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;->()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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/MultipleStartInstructionsTest/classes.dex b/brut.apktool.smali/baksmali/src/test/resources/MultipleStartInstructionsTest/classes.dex new file mode 100644 index 00000000..f6876c29 Binary files /dev/null and b/brut.apktool.smali/baksmali/src/test/resources/MultipleStartInstructionsTest/classes.dex differ diff --git a/brut.apktool.smali/baksmali/src/test/resources/RegisterEqualityOnMergeTest/RegisterEqualityOnMerge.smali b/brut.apktool.smali/baksmali/src/test/resources/RegisterEqualityOnMergeTest/RegisterEqualityOnMerge.smali new file mode 100644 index 00000000..b2b9b521 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/RegisterEqualityOnMergeTest/RegisterEqualityOnMerge.smali @@ -0,0 +1,37 @@ +.class public LRegisterEqualityOnMerge; +.super Ljava/lang/Object; + + +# direct methods +.method public constructor ()V + .registers 4 + + #v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LRegisterEqualityOnMerge;); + invoke-direct {p0}, Ljava/lang/Object;->()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 diff --git a/brut.apktool.smali/baksmali/src/test/resources/RegisterEqualityOnMergeTest/classes.dex b/brut.apktool.smali/baksmali/src/test/resources/RegisterEqualityOnMergeTest/classes.dex new file mode 100644 index 00000000..424fc203 Binary files /dev/null and b/brut.apktool.smali/baksmali/src/test/resources/RegisterEqualityOnMergeTest/classes.dex differ diff --git a/brut.apktool.smali/baksmali/src/test/resources/UninitRefIdentityTest/UninitRefIdentityTest.smali b/brut.apktool.smali/baksmali/src/test/resources/UninitRefIdentityTest/UninitRefIdentityTest.smali new file mode 100644 index 00000000..f9c43637 --- /dev/null +++ b/brut.apktool.smali/baksmali/src/test/resources/UninitRefIdentityTest/UninitRefIdentityTest.smali @@ -0,0 +1,50 @@ +.class public LUninitRefIdentityTest; +.super Ljava/lang/Object; + + +# direct methods +.method public constructor ()V + .registers 4 + + #v0=(Uninit);v1=(Uninit);v2=(Uninit);p0=(UninitThis,LUninitRefIdentityTest;); + invoke-direct {p0}, Ljava/lang/Object;->()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;->()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 (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;->()V + #p0=(Reference,LUninitRefIdentityTest;);p1=(Reference,LUninitRefIdentityTest;); + + #p0=(Reference,LUninitRefIdentityTest;);p1=(Reference,LUninitRefIdentityTest;); + return-void + #p0=(Reference,LUninitRefIdentityTest;);p1=(Reference,LUninitRefIdentityTest;); +.end method diff --git a/brut.apktool.smali/baksmali/src/test/resources/UninitRefIdentityTest/classes.dex b/brut.apktool.smali/baksmali/src/test/resources/UninitRefIdentityTest/classes.dex new file mode 100644 index 00000000..ea146cf6 Binary files /dev/null and b/brut.apktool.smali/baksmali/src/test/resources/UninitRefIdentityTest/classes.dex differ diff --git a/brut.apktool.smali/build.gradle b/brut.apktool.smali/build.gradle new file mode 100644 index 00000000..f45ca80c --- /dev/null +++ b/brut.apktool.smali/build.gradle @@ -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' +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/.gitignore b/brut.apktool.smali/dexlib/.gitignore deleted file mode 100644 index ea8c4bf7..00000000 --- a/brut.apktool.smali/dexlib/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java deleted file mode 100644 index 3882b25d..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationDirectoryItem.java +++ /dev/null @@ -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 { - @Nullable - private AnnotationSetItem classAnnotations; - @Nullable - private FieldAnnotation[] fieldAnnotations; - @Nullable - private MethodAnnotation[] methodAnnotations; - @Nullable - private ParameterAnnotation[] parameterAnnotations; - - /** - * Creates a new uninitialized AnnotationDirectoryItem - * @param dexFile The DexFile that this item belongs to - */ - protected AnnotationDirectoryItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new AnnotationDirectoryItem with the given values - * @param dexFile The DexFile that this item belongs to - * @param classAnnotations The annotations associated with the overall class - * @param fieldAnnotations A list of FieldAnnotation objects that contain the field annotations for - * this class - * @param methodAnnotations A list of MethodAnnotation objects that contain the method annotations for - * this class - * @param parameterAnnotations A list of ParameterAnnotation objects that contain the parameter - * annotations for the methods in this class - */ - private AnnotationDirectoryItem(DexFile dexFile, @Nullable AnnotationSetItem classAnnotations, - @Nullable List fieldAnnotations, - @Nullable List methodAnnotations, - @Nullable List 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 AnnotationDirectoryItem for the given values, and that has been interned into the given - * DexFile - * @param dexFile The DexFile that this item belongs to - * @param classAnnotations The annotations associated with the class - * @param fieldAnnotations A list of FieldAnnotation objects containing the field annotations - * @param methodAnnotations A list of MethodAnnotation objects containing the method annotations - * @param parameterAnnotations A list of ParameterAnnotation objects containin the parameter - * annotations - * @return an AnnotationItem for the given values, and that has been interned into the given - * DexFile - */ - public static AnnotationDirectoryItem internAnnotationDirectoryItem(DexFile dexFile, - AnnotationSetItem classAnnotations, - List fieldAnnotations, - List methodAnnotations, - List 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 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 AnnotationSetItem 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 AnnotationDirectoryItem - * @return A list of FieldAnnotation objects, or null if there are no field annotations - */ - @Nonnull - public List getFieldAnnotations() { - if (fieldAnnotations == null) { - return Collections.emptyList(); - } - return ReadOnlyArrayList.of(fieldAnnotations); - } - - /** - * Get a list of the method annotations in this AnnotationDirectoryItem - * @return A list of MethodAnnotation objects, or null if there are no method annotations - */ - @Nonnull - public List getMethodAnnotations() { - if (methodAnnotations == null) { - return Collections.emptyList(); - } - return ReadOnlyArrayList.of(methodAnnotations); - } - - /** - * Get a list of the parameter annotations in this AnnotationDirectoryItem - * @return A list of ParameterAnnotation objects, or null if there are no parameter annotations - */ - @Nonnull - public List 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 AnnotationSetItem 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 AnnotationSetItem 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 AnnotationSetRefList 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 AnnotationDirectoryItem - */ - public int getFieldAnnotationCount() { - if (fieldAnnotations == null) { - return 0; - } - return fieldAnnotations.length; - } - - /** - * @return The number of method annotations in this AnnotationDirectoryItem - */ - public int getMethodAnnotationCount() { - if (methodAnnotations == null) { - return 0; - } - return methodAnnotations.length; - } - - /** - * @return The number of parameter annotations in this AnnotationDirectoryItem - */ - 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 { - public final FieldIdItem field; - public final AnnotationSetItem annotationSet; - - public FieldAnnotation(FieldIdItem field, AnnotationSetItem annotationSet) { - this.field = field; - this.annotationSet = annotationSet; - } - - public int compareTo(Convertible 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 { - public final MethodIdItem method; - public final AnnotationSetItem annotationSet; - - public MethodAnnotation(MethodIdItem method, AnnotationSetItem annotationSet) { - this.method = method; - this.annotationSet = annotationSet; - } - - public int compareTo(Convertible 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 { - public final MethodIdItem method; - public final AnnotationSetRefList annotationSet; - - public ParameterAnnotation(MethodIdItem method, AnnotationSetRefList annotationSet) { - this.method = method; - this.annotationSet = annotationSet; - } - - public int compareTo(Convertible 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; - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java deleted file mode 100644 index f35a414b..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationItem.java +++ /dev/null @@ -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 { - private int hashCode = 0; - - private AnnotationVisibility visibility; - private AnnotationEncodedSubValue annotationValue; - - /** - * Creates a new uninitialized AnnotationItem - * @param dexFile The DexFile that this item belongs to - */ - protected AnnotationItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new AnnotationItem with the given values - * @param dexFile The DexFile 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 AnnotationItem for the given values, and that has been interned into the given - * DexFile - * @param dexFile The DexFile that this item belongs to - * @param visibility The visibility of this annotation - * @param annotationValue The value of this annotation - * @return an AnnotationItem for the given values, and that has been interned into the given - * DexFile - */ - 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); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java deleted file mode 100644 index e221ad02..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetItem.java +++ /dev/null @@ -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 { - private int hashCode = 0; - - private AnnotationItem[] annotations; - - /** - * Creates a new uninitialized AnnotationSetItem - * @param dexFile The DexFile that this item belongs to - */ - protected AnnotationSetItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new AnnotationSetItem for the given annotations - * @param dexFile The DexFile that this item belongs to - * @param annotations The annotations for this AnnotationSetItem - */ - private AnnotationSetItem(DexFile dexFile, AnnotationItem[] annotations) { - super(dexFile); - this.annotations = annotations; - } - - /** - * Returns an AnnotationSetItem for the given annotations, and that has been interned into the given - * DexFile - * @param dexFile The DexFile that this item belongs to - * @param annotations The annotations for this AnnotationSetItem - * @return an AnnotationSetItem for the given annotations - */ - public static AnnotationSetItem internAnnotationSetItem(DexFile dexFile, List 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() { - 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; iAnnotationItem objects in this AnnotationSetItem - */ - 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); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java deleted file mode 100644 index e38ce31c..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationSetRefList.java +++ /dev/null @@ -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 { - private int hashCode = 0; - - private AnnotationSetItem[] annotationSets; - - /** - * Creates a new uninitialized AnnotationSetRefList - * @param dexFile The DexFile that this item belongs to - */ - protected AnnotationSetRefList(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new AnnotationSetRefList for the given annotation sets - * @param dexFile The DexFile that this item belongs to - * @param annotationSets The annotationSets for this AnnotationSetRefList - */ - private AnnotationSetRefList(DexFile dexFile, AnnotationSetItem[] annotationSets) { - super(dexFile); - this.annotationSets = annotationSets; - } - - /** - * Returns an AnnotationSetRefList for the given annotation sets, and that has been interned into the - * given DexFile - * @param dexFile The DexFile that this item belongs to - * @param annotationSets The annotation sets for this AnnotationSetRefList - * @return an AnnotationSetItem for the given annotations - */ - public static AnnotationSetRefList internAnnotationSetRefList(DexFile dexFile, - List 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; iAnnotationSetItem objects that make up this - * AnnotationSetRefList - */ - 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); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java deleted file mode 100644 index 03ae96f0..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/AnnotationVisibility.java +++ /dev/null @@ -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); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java deleted file mode 100644 index 7afba479..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDataItem.java +++ /dev/null @@ -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 { - @Nullable - private EncodedField[] staticFields = null; - @Nullable - private EncodedField[] instanceFields = null; - @Nullable - private EncodedMethod[] directMethods = null; - @Nullable - private EncodedMethod[] virtualMethods = null; - - /** - * Creates a new uninitialized ClassDataItem - * @param dexFile The DexFile that this item belongs to - */ - public ClassDataItem(final DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new ClassDataItem with the given values - * @param dexFile The DexFile 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 ClassDataItem with the given values - * @param dexFile The DexFile 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 ClassDataItem with the given values - */ - public static ClassDataItem internClassDataItem(DexFile dexFile, @Nullable List staticFields, - @Nullable List instanceFields, - @Nullable List directMethods, - @Nullable List virtualMethods) { - EncodedField[] staticFieldsArray = null; - EncodedField[] instanceFieldsArray = null; - EncodedMethod[] directMethodsArray = null; - EncodedMethod[] virtualMethodsArray = null; - - if (staticFields != null && staticFields.size() > 0) { - SortedSet staticFieldsSet = new TreeSet(); - 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 instanceFieldsSet = new TreeSet(); - 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 directMethodSet = new TreeSet(); - - 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 virtualMethodSet = new TreeSet(); - 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 0) { - instanceFields = new EncodedField[instanceFieldsCount]; - EncodedField previousEncodedField = null; - for (int i=0; i 0) { - directMethods = new EncodedMethod[directMethodsCount]; - EncodedMethod previousEncodedMethod = null; - for (int i=0; i 0) { - virtualMethods = new EncodedMethod[virtualMethodsCount]; - EncodedMethod previousEncodedMethod = null; - for (int i=0; i 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 getStaticFields() { - if (staticFields == null) { - return Collections.emptyList(); - } - return ReadOnlyArrayList.of(staticFields); - } - - /** - * @return the instance fields for this class - */ - @Nonnull - public List getInstanceFields() { - if (instanceFields == null) { - return Collections.emptyList(); - } - return ReadOnlyArrayList.of(instanceFields); - } - - /** - * @return the direct methods for this class - */ - @Nonnull - public List getDirectMethods() { - if (directMethods == null) { - return Collections.emptyList(); - } - return ReadOnlyArrayList.of(directMethods); - } - - /** - * @return the virtual methods for this class - */ - @Nonnull - public List getVirtualMethods() { - if (virtualMethods == null) { - return Collections.emptyList(); - } - return ReadOnlyArrayList.of(virtualMethods); - } - - /** - * @return The number of static fields in this ClassDataItem - */ - public int getStaticFieldCount() { - if (staticFields == null) { - return 0; - } - return staticFields.length; - } - - /** - * @return The number of instance fields in this ClassDataItem - */ - public int getInstanceFieldCount() { - if (instanceFields == null) { - return 0; - } - return instanceFields.length; - } - - /** - * @return The number of direct methods in this ClassDataItem - */ - public int getDirectMethodCount() { - if (directMethods == null) { - return 0; - } - return directMethods.length; - } - - /** - * @return The number of virtual methods in this ClassDataItem - */ - 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>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 { - /** - * The FieldIdItem that this EncodedField is associated with - */ - public final FieldIdItem field; - - /** - * The access flags for this field - */ - public final int accessFlags; - - /** - * Constructs a new EncodedField with the given values - * @param field The FieldIdItem that this EncodedField 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 EncodedField while reading in a DexFile - * @param dexFile The DexFile that is being read in - * @param in the Input object to read the EncodedField from - * @param previousEncodedField The previous EncodedField in the list containing this - * EncodedField. - */ - 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 EncodedField to the given AnnotatedOutput object - * @param out the AnnotatedOutput object to write to - * @param previousEncodedField The previous EncodedField in the list containing this - * EncodedField. - */ - 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 EncodedField and returns the offset - * immediately following it - * @param offset the offset of this EncodedField in the DexFile - * @param previousEncodedField The previous EncodedField in the list containing this - * EncodedField. - * @return the offset immediately following this EncodedField - */ - 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 EncodedField to another, based on the comparison of the associated - * FieldIdItem - * @param other The EncodedField 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 EncodedField is equal to other, based on the equality of the associated - * FieldIdItem - * @param other The EncodedField 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 { - /** - * The MethodIdItem that this EncodedMethod is associated with - */ - public final MethodIdItem method; - - /** - * The access flags for this method - */ - public final int accessFlags; - - /** - * The CodeItem 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 EncodedMethod with the given values - * @param method The MethodIdItem that this EncodedMethod is associated with - * @param accessFlags The access flags for this method - * @param codeItem The CodeItem 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 EncodedMethod while reading in a DexFile - * @param dexFile The DexFile that is being read in - * @param readContext a ReadContext object to hold information that is only needed while reading - * in a file - * @param in the Input object to read the EncodedMethod from - * @param previousEncodedMethod The previous EncodedMethod in the list containing this - * EncodedMethod. - */ - 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 EncodedMethod to the given AnnotatedOutput object - * @param out the AnnotatedOutput object to write to - * @param previousEncodedMethod The previous EncodedMethod in the list containing this - * EncodedMethod. - */ - 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 EncodedMethod and returns the offset - * immediately following it - * @param offset the offset of this EncodedMethod in the DexFile - * @param previousEncodedMethod The previous EncodedMethod in the list containing this - * EncodedMethod. - * @return the offset immediately following this EncodedField - */ - 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 EncodedMethod to another, based on the comparison of the associated - * MethodIdItem - * @param other The EncodedMethod 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 EncodedMethod is equal to other, based on the equality of the associated - * MethodIdItem - * @param other The EncodedMethod 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); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java deleted file mode 100644 index 9664b997..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ClassDefItem.java +++ /dev/null @@ -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 { - 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 ClassDefItem - * @param dexFile The DexFile that this item belongs to - */ - protected ClassDefItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new ClassDefItem with the given values - * @param dexFile The DexFile 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 ClassDataItem 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 ClassDataItem. 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 ClassDefItem for the given values, and that has been interned into the given - * DexFile - * @param dexFile The DexFile 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 ClassDataItem 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 - * StaticFieldInitializer 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 ClassDefItem for the given values, and that has been interned into the given - * DexFile - */ - 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 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 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 section; - private final HashMap unplacedClassDefsByType = - new HashMap(); - - private int currentIndex = 0; - private int currentOffset; - - public ClassDefPlacer(IndexedSection 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() { - 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 { - 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 DexFile - * @param staticFieldInitializers the initial values - * @return an interned EncodedArrayItem containing the static field initializers - */ - private static EncodedArrayItem makeStaticFieldInitializersItem(DexFile dexFile, - @Nonnull List 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(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); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java deleted file mode 100644 index 794cec60..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java +++ /dev/null @@ -1,1348 +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 org.jf.dexlib.Util.AccessFlags; -import org.jf.dexlib.Util.ExceptionWithContext; -import org.jf.dexlib.Util.SparseArray; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.File; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static org.jf.dexlib.ClassDataItem.EncodedField; -import static org.jf.dexlib.ClassDataItem.EncodedMethod; - -public class ClassPath { - private static ClassPath theClassPath = null; - - /** - * The current version of dalvik in master(AOSP) has a slight change to the way the - * virtual tables are computed. This should be set to true to use the new logic. - * TODO: set this based on api level, once it's present in a released version of Android - */ - private boolean checkPackagePrivateAccess; - - private final HashMap classDefs; - protected ClassDef javaLangObjectClassDef; //cached ClassDef for Ljava/lang/Object; - - // Contains the classes that we haven't loaded yet - private HashMap unloadedClasses; - - private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$"); - - /** - * Initialize the class path using the dependencies from an odex file - * @param classPathDirs The directories to search for boot class path files - * @param extraBootClassPathEntries any extra entries that should be added after the entries that are read - * from the odex file - * @param dexFilePath The path of the dex file (used for error reporting purposes only) - * @param dexFile The DexFile to load - it must represents an odex file - */ - public static void InitializeClassPathFromOdex(String[] classPathDirs, String[] extraBootClassPathEntries, - String dexFilePath, DexFile dexFile, - boolean checkPackagePrivateAccess) { - if (!dexFile.isOdex()) { - throw new ExceptionWithContext("Cannot use InitialiazeClassPathFromOdex with a non-odex DexFile"); - } - - if (theClassPath != null) { - throw new ExceptionWithContext("Cannot initialize ClassPath multiple times"); - } - - OdexDependencies odexDependencies = dexFile.getOdexDependencies(); - - String[] bootClassPath = new String[odexDependencies.getDependencyCount()]; - for (int i=0; i(); - } - - private void initClassPath(String[] classPathDirs, String[] bootClassPath, String[] extraBootClassPathEntries, - String dexFilePath, DexFile dexFile, boolean checkPackagePrivateAccess) { - this.checkPackagePrivateAccess = checkPackagePrivateAccess; - unloadedClasses = new LinkedHashMap(); - - if (bootClassPath != null) { - for (String bootClassPathEntry: bootClassPath) { - loadBootClassPath(classPathDirs, bootClassPathEntry); - } - } - - if (extraBootClassPathEntries != null) { - for (String bootClassPathEntry: extraBootClassPathEntries) { - loadBootClassPath(classPathDirs, bootClassPathEntry); - } - } - - if (dexFile != null) { - loadDexFile(dexFilePath, dexFile); - } - - javaLangObjectClassDef = getClassDef("Ljava/lang/Object;", false); - - for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) { - ClassDef classDef = new PrimitiveClassDef(primitiveType); - classDefs.put(primitiveType, classDef); - } - } - - private void loadBootClassPath(String[] classPathDirs, String bootClassPathEntry) { - for (String classPathDir: classPathDirs) { - File file = null; - DexFile dexFile = null; - - int extIndex = bootClassPathEntry.lastIndexOf("."); - - String baseEntry; - if (extIndex == -1) { - baseEntry = bootClassPathEntry; - } else { - baseEntry = bootClassPathEntry.substring(0, extIndex); - } - - for (String ext: new String[]{"", ".odex", ".jar", ".apk", ".zip"}) { - if (ext.length() == 0) { - file = new File(classPathDir, bootClassPathEntry); - } else { - file = new File(classPathDir, baseEntry + ext); - } - - if (file.exists()) { - if (!file.canRead()) { - System.err.println(String.format("warning: cannot open %s for reading. Will continue " + - "looking.", file.getPath())); - continue; - } - - try { - dexFile = new DexFile(file, false, true); - } catch (DexFile.NoClassesDexException ex) { - continue; - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, "Error while reading boot class path entry \"" + - bootClassPathEntry + "\"."); - } - } - } - if (dexFile == null) { - continue; - } - - try { - loadDexFile(file.getPath(), dexFile); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, - String.format("Error while loading boot classpath entry %s", bootClassPathEntry)); - } - return; - } - throw new ExceptionWithContext(String.format("Cannot locate boot class path file %s", bootClassPathEntry)); - } - - private void loadDexFile(String dexFilePath, DexFile dexFile) { - for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) { - try { - UnresolvedClassInfo unresolvedClassInfo = new UnresolvedClassInfo(dexFilePath, classDefItem); - - if (!unloadedClasses.containsKey(unresolvedClassInfo.classType)) { - unloadedClasses.put(unresolvedClassInfo.classType, unresolvedClassInfo); - } - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s", - classDefItem.getClassType().getTypeDescriptor())); - } - } - } - - /** - * This method loads the given class (and any dependent classes, as needed), removing them from unloadedClasses - * @param classType the class to load - * @return the newly loaded ClassDef object for the given class, or null if the class cannot be found - */ - @Nullable - private static ClassDef loadClassDef(String classType) { - ClassDef classDef = null; - - UnresolvedClassInfo classInfo = theClassPath.unloadedClasses.get(classType); - if (classInfo == null) { - return null; - } - - try { - classDef = new ClassDef(classInfo); - theClassPath.classDefs.put(classDef.classType, classDef); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s from file %s", - classInfo.classType, classInfo.dexFilePath)); - } - theClassPath.unloadedClasses.remove(classType); - - return classDef; - } - - @Nonnull - public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) { - ClassDef classDef = theClassPath.classDefs.get(classType); - if (classDef == null) { - //if it's an array class, try to create it - if (classType.charAt(0) == '[') { - return theClassPath.createArrayClassDef(classType); - } else { - try { - classDef = loadClassDef(classType); - if (classDef == null) { - throw new ExceptionWithContext( - String.format("Could not find definition for class %s", classType)); - } - } catch (Exception ex) { - RuntimeException exWithContext = ExceptionWithContext.withContext(ex, - String.format("Error while loading ClassPath class %s", classType)); - if (createUnresolvedClassDef) { - //TODO: add warning message - return theClassPath.createUnresolvedClassDef(classType); - } else { - throw exWithContext; - } - } - } - } - return classDef; - } - - public static ClassDef getClassDef(String classType) { - return getClassDef(classType, true); - } - - public static ClassDef getClassDef(TypeIdItem classType) { - return getClassDef(classType.getTypeDescriptor()); - } - - public static ClassDef getClassDef(TypeIdItem classType, boolean creatUnresolvedClassDef) { - return getClassDef(classType.getTypeDescriptor(), creatUnresolvedClassDef); - } - - //256 [ characters - private static final String arrayPrefix = "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + - "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + - "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["; - private static ClassDef getArrayClassDefByElementClassAndDimension(ClassDef classDef, int arrayDimension) { - return getClassDef(arrayPrefix.substring(256 - arrayDimension) + classDef.classType); - } - - private static ClassDef unresolvedObjectClassDef = null; - public static ClassDef getUnresolvedObjectClassDef() { - if (unresolvedObjectClassDef == null) { - unresolvedObjectClassDef = new UnresolvedClassDef("Ljava/lang/Object;"); - } - return unresolvedObjectClassDef; - } - - private ClassDef createUnresolvedClassDef(String classType) { - assert classType.charAt(0) == 'L'; - - UnresolvedClassDef unresolvedClassDef = new UnresolvedClassDef(classType); - classDefs.put(classType, unresolvedClassDef); - return unresolvedClassDef; - } - - private ClassDef createArrayClassDef(String arrayClassName) { - assert arrayClassName != null; - assert arrayClassName.charAt(0) == '['; - - ArrayClassDef arrayClassDef = new ArrayClassDef(arrayClassName); - if (arrayClassDef.elementClass == null) { - return null; - } - - classDefs.put(arrayClassName, arrayClassDef); - return arrayClassDef; - } - - public static ClassDef getCommonSuperclass(ClassDef class1, ClassDef class2) { - if (class1 == class2) { - return class1; - } - - if (class1 == null) { - return class2; - } - - if (class2 == null) { - return class1; - } - - //TODO: do we want to handle primitive types here? I don't think so.. (if not, add assert) - - if (class2.isInterface) { - if (class1.implementsInterface(class2)) { - return class2; - } - return theClassPath.javaLangObjectClassDef; - } - - if (class1.isInterface) { - if (class2.implementsInterface(class1)) { - return class1; - } - return theClassPath.javaLangObjectClassDef; - } - - if (class1 instanceof ArrayClassDef && class2 instanceof ArrayClassDef) { - return getCommonArraySuperclass((ArrayClassDef)class1, (ArrayClassDef)class2); - } - - //we've got two non-array reference types. Find the class depth of each, and then move up the longer one - //so that both classes are at the same class depth, and then move each class up until they match - - //we don't strictly need to keep track of the class depth separately, but it's probably slightly faster - //to do so, rather than calling getClassDepth() many times - int class1Depth = class1.getClassDepth(); - int class2Depth = class2.getClassDepth(); - - while (class1Depth > class2Depth) { - class1 = class1.superclass; - class1Depth--; - } - - while (class2Depth > class1Depth) { - class2 = class2.superclass; - class2Depth--; - } - - while (class1Depth > 0) { - if (class1 == class2) { - return class1; - } - class1 = class1.superclass; - class1Depth--; - class2 = class2.superclass; - class2Depth--; - } - - return class1; - } - - private static ClassDef getCommonArraySuperclass(ArrayClassDef class1, ArrayClassDef class2) { - assert class1 != class2; - - //If one of the arrays is a primitive array, then the only option is to return java.lang.Object - //TODO: might it be possible to merge something like int[] and short[] into int[]? (I don't think so..) - if (class1.elementClass instanceof PrimitiveClassDef || class2.elementClass instanceof PrimitiveClassDef) { - return theClassPath.javaLangObjectClassDef; - } - - //if the two arrays have the same number of dimensions, then we should return an array class with the - //same number of dimensions, for the common superclass of the 2 element classes - if (class1.arrayDimensions == class2.arrayDimensions) { - ClassDef commonElementClass; - if (class1.elementClass instanceof UnresolvedClassDef || - class2.elementClass instanceof UnresolvedClassDef) { - commonElementClass = ClassPath.getUnresolvedObjectClassDef(); - } else { - commonElementClass = getCommonSuperclass(class1.elementClass, class2.elementClass); - } - return getArrayClassDefByElementClassAndDimension(commonElementClass, class1.arrayDimensions); - } - - //something like String[][][] and String[][] should be merged to Object[][] - //this also holds when the element classes aren't the same (but are both reference types) - int dimensions = Math.min(class1.arrayDimensions, class2.arrayDimensions); - return getArrayClassDefByElementClassAndDimension(theClassPath.javaLangObjectClassDef, dimensions); - } - - public static class ArrayClassDef extends ClassDef { - private final ClassDef elementClass; - private final int arrayDimensions; - - protected ArrayClassDef(String arrayClassType) { - super(arrayClassType, ClassDef.ArrayClassDef); - assert arrayClassType.charAt(0) == '['; - - int i=0; - while (arrayClassType.charAt(i) == '[') i++; - - String elementClassType = arrayClassType.substring(i); - - if (i>256) { - throw new ExceptionWithContext("Error while creating array class for element type " + elementClassType + - " with " + i + " dimensions. The maximum number of dimensions is 256"); - } - - try { - elementClass = ClassPath.getClassDef(arrayClassType.substring(i)); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, "Error while creating array class " + arrayClassType); - } - arrayDimensions = i; - } - - /** - * Returns the "base" element class of the array. - * - * For example, for a multi-dimensional array of strings ([[Ljava/lang/String;), this method would return - * Ljava/lang/String; - * @return the "base" element class of the array - */ - public ClassDef getBaseElementClass() { - return elementClass; - } - - /** - * Returns the "immediate" element class of the array. - * - * For example, for a multi-dimensional array of stings with 2 dimensions ([[Ljava/lang/String;), this method - * would return [Ljava/lang/String; - * @return the immediate element class of the array - */ - public ClassDef getImmediateElementClass() { - if (arrayDimensions == 1) { - return elementClass; - } - return getArrayClassDefByElementClassAndDimension(elementClass, arrayDimensions - 1); - } - - public int getArrayDimensions() { - return arrayDimensions; - } - - @Override - public boolean extendsClass(ClassDef superclassDef) { - if (!(superclassDef instanceof ArrayClassDef)) { - if (superclassDef == ClassPath.theClassPath.javaLangObjectClassDef) { - return true; - } else if (superclassDef.isInterface) { - return this.implementsInterface(superclassDef); - } - return false; - } - - ArrayClassDef arraySuperclassDef = (ArrayClassDef)superclassDef; - if (this.arrayDimensions == arraySuperclassDef.arrayDimensions) { - ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass(); - - if (baseElementClass.isInterface) { - return true; - } - - return baseElementClass.extendsClass(arraySuperclassDef.getBaseElementClass()); - } else if (this.arrayDimensions > arraySuperclassDef.arrayDimensions) { - ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass(); - if (baseElementClass.isInterface) { - return true; - } - - if (baseElementClass == ClassPath.theClassPath.javaLangObjectClassDef) { - return true; - } - return false; - } - return false; - } - } - - public static class PrimitiveClassDef extends ClassDef { - protected PrimitiveClassDef(String primitiveClassType) { - super(primitiveClassType, ClassDef.PrimitiveClassDef); - assert primitiveClassType.charAt(0) != 'L' && primitiveClassType.charAt(0) != '['; - } - } - - public static class UnresolvedClassDef extends ClassDef { - protected UnresolvedClassDef(String unresolvedClassDef) { - super(unresolvedClassDef, ClassDef.UnresolvedClassDef); - assert unresolvedClassDef.charAt(0) == 'L'; - } - - protected ValidationException unresolvedValidationException() { - return new ValidationException(String.format("class %s cannot be resolved.", this.getClassType())); - } - - public ClassDef getSuperclass() { - return theClassPath.javaLangObjectClassDef; - } - - public int getClassDepth() { - throw unresolvedValidationException(); - } - - public boolean isInterface() { - throw unresolvedValidationException(); - } - - public boolean extendsClass(ClassDef superclassDef) { - if (superclassDef != theClassPath.javaLangObjectClassDef && superclassDef != this) { - throw unresolvedValidationException(); - } - return true; - } - - public boolean implementsInterface(ClassDef interfaceDef) { - throw unresolvedValidationException(); - } - - public boolean hasVirtualMethod(String method) { - if (!super.hasVirtualMethod(method)) { - throw unresolvedValidationException(); - } - return true; - } - } - - public static class FieldDef { - public final String definingClass; - public final String name; - public final String type; - - public FieldDef(String definingClass, String name, String type) { - this.definingClass = definingClass; - this.name = name; - this.type = type; - } - } - - public static class ClassDef implements Comparable { - private final String classType; - private final ClassDef superclass; - /** - * This is a list of all of the interfaces that a class implements, either directly or indirectly. It includes - * all interfaces implemented by the superclass, and all super-interfaces of any implemented interface. The - * intention is to make it easier to determine whether the class implements a given interface or not. - */ - private final TreeSet implementedInterfaces; - - private final boolean isInterface; - - private final int classDepth; - - // classes can only be public or package-private. Internally, any private/protected inner class is actually - // package-private. - private final boolean isPublic; - - private final VirtualMethod[] vtable; - - //this maps a method name of the form method(III)Ljava/lang/String; to an integer - //If the value is non-negative, it is a vtable index - //If it is -1, it is a non-static direct method, - //If it is -2, it is a static method - private final HashMap methodLookup; - - private final SparseArray instanceFields; - - public final static int ArrayClassDef = 0; - public final static int PrimitiveClassDef = 1; - public final static int UnresolvedClassDef = 2; - - private final static int DirectMethod = -1; - private final static int StaticMethod = -2; - - /** - * The following fields are used only during the initial loading of classes, and are set to null afterwards - * TODO: free these - */ - - //This is only the virtual methods that this class declares itself. - private VirtualMethod[] virtualMethods; - //this is a list of all the interfaces that the class implements directory, or any super interfaces of those - //interfaces. It is generated in such a way that it is ordered in the same way as dalvik's ClassObject.iftable, - private LinkedHashMap interfaceTable; - - /** - * This constructor is used for the ArrayClassDef, PrimitiveClassDef and UnresolvedClassDef subclasses - * @param classType the class type - * @param classFlavor one of ArrayClassDef, PrimitiveClassDef or UnresolvedClassDef - */ - protected ClassDef(String classType, int classFlavor) { - if (classFlavor == ArrayClassDef) { - assert classType.charAt(0) == '['; - this.classType = classType; - this.superclass = ClassPath.theClassPath.javaLangObjectClassDef; - implementedInterfaces = new TreeSet(); - implementedInterfaces.add(ClassPath.getClassDef("Ljava/lang/Cloneable;")); - implementedInterfaces.add(ClassPath.getClassDef("Ljava/io/Serializable;")); - isInterface = false; - isPublic = true; - - vtable = superclass.vtable; - methodLookup = superclass.methodLookup; - - instanceFields = superclass.instanceFields; - classDepth = 1; //1 off from java.lang.Object - - virtualMethods = null; - interfaceTable = null; - } else if (classFlavor == PrimitiveClassDef) { - //primitive type - assert classType.charAt(0) != '[' && classType.charAt(0) != 'L'; - - this.classType = classType; - this.superclass = null; - implementedInterfaces = null; - isInterface = false; - isPublic = true; - vtable = null; - methodLookup = null; - instanceFields = null; - classDepth = 0; //TODO: maybe use -1 to indicate not applicable? - - virtualMethods = null; - interfaceTable = null; - } else /*if (classFlavor == UnresolvedClassDef)*/ { - assert classType.charAt(0) == 'L'; - this.classType = classType; - this.superclass = ClassPath.getClassDef("Ljava/lang/Object;"); - implementedInterfaces = new TreeSet(); - isInterface = false; - isPublic = true; - - vtable = superclass.vtable; - methodLookup = superclass.methodLookup; - - instanceFields = superclass.instanceFields; - classDepth = 1; //1 off from java.lang.Object - - virtualMethods = null; - interfaceTable = null; - } - } - - protected ClassDef(UnresolvedClassInfo classInfo) { - classType = classInfo.classType; - isPublic = classInfo.isPublic; - isInterface = classInfo.isInterface; - - superclass = loadSuperclass(classInfo); - if (superclass == null) { - classDepth = 0; - } else { - classDepth = superclass.classDepth + 1; - } - - implementedInterfaces = loadAllImplementedInterfaces(classInfo); - - //TODO: we can probably get away with only creating the interface table for interface types - interfaceTable = loadInterfaceTable(classInfo); - virtualMethods = classInfo.virtualMethods; - vtable = loadVtable(classInfo); - - int directMethodCount = 0; - if (classInfo.directMethods != null) { - directMethodCount = classInfo.directMethods.length; - } - methodLookup = new HashMap((int)Math.ceil(((vtable.length + directMethodCount)/ .7f)), .75f); - for (int i=0; i 0) { - for (int i=0; i superclassDepth) { - ancestor = ancestor.getSuperclass(); - } - - return ancestor == superclassDef; - } - - /** - * Returns true if this class implements the given interface. This searches the interfaces that this class - * directly implements, any interface implemented by this class's superclasses, and any super-interface of - * any of these interfaces. - * @param interfaceDef the interface - * @return true if this class implements the given interface - */ - public boolean implementsInterface(ClassDef interfaceDef) { - assert !(interfaceDef instanceof UnresolvedClassDef); - return implementedInterfaces.contains(interfaceDef); - } - - public boolean hasVirtualMethod(String method) { - Integer val = methodLookup.get(method); - if (val == null || val < 0) { - return false; - } - return true; - } - - public int getMethodType(String method) { - Integer val = methodLookup.get(method); - if (val == null) { - return -1; - } - if (val >= 0) { - return DeodexUtil.Virtual; - } - if (val == DirectMethod) { - return DeodexUtil.Direct; - } - if (val == StaticMethod) { - return DeodexUtil.Static; - } - throw new RuntimeException("Unexpected method type"); - } - - public FieldDef getInstanceField(int fieldOffset) { - return this.instanceFields.get(fieldOffset, null); - } - - public String getVirtualMethod(int vtableIndex) { - if (vtableIndex < 0 || vtableIndex >= vtable.length) { - return null; - } - return this.vtable[vtableIndex].method; - } - - private void swap(byte[] fieldTypes, FieldDef[] fields, int position1, int position2) { - byte tempType = fieldTypes[position1]; - fieldTypes[position1] = fieldTypes[position2]; - fieldTypes[position2] = tempType; - - FieldDef tempField = fields[position1]; - fields[position1] = fields[position2]; - fields[position2] = tempField; - } - - private ClassDef loadSuperclass(UnresolvedClassInfo classInfo) { - if (classInfo.classType.equals("Ljava/lang/Object;")) { - if (classInfo.superclassType != null) { - throw new ExceptionWithContext("Invalid superclass " + - classInfo.superclassType + " for Ljava/lang/Object;. " + - "The Object class cannot have a superclass"); - } - return null; - } else { - String superclassType = classInfo.superclassType; - if (superclassType == null) { - throw new ExceptionWithContext(classInfo.classType + " has no superclass"); - } - - ClassDef superclass; - try { - superclass = ClassPath.getClassDef(superclassType); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, - String.format("Could not find superclass %s", superclassType)); - } - - if (!isInterface && superclass.isInterface) { - throw new ValidationException("Class " + classType + " has the interface " + superclass.classType + - " as its superclass"); - } - if (isInterface && !superclass.isInterface && superclass != - ClassPath.theClassPath.javaLangObjectClassDef) { - throw new ValidationException("Interface " + classType + " has the non-interface class " + - superclass.classType + " as its superclass"); - } - - return superclass; - } - } - - private TreeSet loadAllImplementedInterfaces(UnresolvedClassInfo classInfo) { - assert classType != null; - assert classType.equals("Ljava/lang/Object;") || superclass != null; - assert classInfo != null; - - TreeSet implementedInterfaceSet = new TreeSet(); - - if (superclass != null) { - for (ClassDef interfaceDef: superclass.implementedInterfaces) { - implementedInterfaceSet.add(interfaceDef); - } - } - - - if (classInfo.interfaces != null) { - for (String interfaceType: classInfo.interfaces) { - ClassDef interfaceDef; - try { - interfaceDef = ClassPath.getClassDef(interfaceType); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, - String.format("Could not find interface %s", interfaceType)); - } - assert interfaceDef.isInterface(); - implementedInterfaceSet.add(interfaceDef); - - interfaceDef = interfaceDef.getSuperclass(); - while (!interfaceDef.getClassType().equals("Ljava/lang/Object;")) { - assert interfaceDef.isInterface(); - implementedInterfaceSet.add(interfaceDef); - interfaceDef = interfaceDef.getSuperclass(); - } - } - } - - return implementedInterfaceSet; - } - - private LinkedHashMap loadInterfaceTable(UnresolvedClassInfo classInfo) { - if (classInfo.interfaces == null) { - return null; - } - - LinkedHashMap interfaceTable = new LinkedHashMap(); - - for (String interfaceType: classInfo.interfaces) { - if (!interfaceTable.containsKey(interfaceType)) { - ClassDef interfaceDef; - try { - interfaceDef = ClassPath.getClassDef(interfaceType); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, - String.format("Could not find interface %s", interfaceType)); - } - interfaceTable.put(interfaceType, interfaceDef); - - if (interfaceDef.interfaceTable != null) { - for (ClassDef superInterface: interfaceDef.interfaceTable.values()) { - if (!interfaceTable.containsKey(superInterface.classType)) { - interfaceTable.put(superInterface.classType, superInterface); - } - } - } - } - } - - return interfaceTable; - } - - //TODO: check the case when we have a package private method that overrides an interface method - private VirtualMethod[] loadVtable(UnresolvedClassInfo classInfo) { - //TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry - List virtualMethodList = new LinkedList(); - - //copy the virtual methods from the superclass - int methodIndex = 0; - if (superclass != null) { - for (int i=0; i vtable) { - for (VirtualMethod virtualMethod: localMethods) { - boolean found = false; - for (int i=0; i loadFields(UnresolvedClassInfo classInfo) { - //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to - //arrange fields, so that we end up with the same field offsets (which is needed for deodexing). - //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets() - - final byte REFERENCE = 0; - final byte WIDE = 1; - final byte OTHER = 2; - - FieldDef[] fields = null; - //the "type" for each field in fields. 0=reference,1=wide,2=other - byte[] fieldTypes = null; - - if (classInfo.instanceFields != null) { - fields = new FieldDef[classInfo.instanceFields.length]; - fieldTypes = new byte[fields.length]; - - for (int i=0; i front) { - if (fieldTypes[back] == REFERENCE) { - swap(fieldTypes, fields, front, back--); - break; - } - back--; - } - } - - if (fieldTypes[front] != REFERENCE) { - break; - } - } - - - int startFieldOffset = 8; - if (this.superclass != null) { - startFieldOffset = this.superclass.getNextFieldOffset(); - } - - int fieldIndexMod; - if ((startFieldOffset % 8) == 0) { - fieldIndexMod = 0; - } else { - fieldIndexMod = 1; - } - - //next, we need to group all the wide fields after the reference fields. But the wide fields have to be - //8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field - //is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in. - //If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets - if (front < fields.length && (front % 2) != fieldIndexMod) { - if (fieldTypes[front] == WIDE) { - //we need to swap in a 32-bit field, so the wide fields will be correctly aligned - back = fields.length - 1; - while (back > front) { - if (fieldTypes[back] == OTHER) { - swap(fieldTypes, fields, front++, back); - break; - } - back--; - } - } else { - //there's already a 32-bit field here that we can use - front++; - } - } - - //do the swap thing for wide fields - back = fields.length - 1; - for (; front front) { - if (fieldTypes[back] == WIDE) { - swap(fieldTypes, fields, front, back--); - break; - } - back--; - } - } - - if (fieldTypes[front] != WIDE) { - break; - } - } - - int superFieldCount = 0; - if (superclass != null) { - superFieldCount = superclass.instanceFields.size(); - } - - //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets - int totalFieldCount = superFieldCount + fields.length; - SparseArray instanceFields = new SparseArray(totalFieldCount); - - int fieldOffset; - - if (superclass != null && superFieldCount > 0) { - for (int i=0; i types = typeList.getTypes(); - if (types != null && types.size() > 0) { - String[] interfaces = new String[types.size()]; - for (int i=0; i encodedMethods = classDataItem.getDirectMethods(); - if (encodedMethods.size() > 0) { - boolean[] staticMethods = new boolean[encodedMethods.size()]; - String[] directMethods = new String[encodedMethods.size()]; - - for (int i=0; i encodedMethods = classDataItem.getVirtualMethods(); - if (encodedMethods.size() > 0) { - VirtualMethod[] virtualMethods = new VirtualMethod[encodedMethods.size()]; - for (int i=0; i encodedFields = classDataItem.getInstanceFields(); - if (encodedFields.size() > 0) { - String[][] instanceFields = new String[encodedFields.size()][2]; - for (int i=0; i paramList = new LinkedList(); - - for (int i=0; i 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 parents = new ArrayList(); - 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); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java deleted file mode 100644 index ca442687..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/DexFileClassMap.java +++ /dev/null @@ -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 definedClasses = new HashMap(); - - 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()); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java deleted file mode 100644 index a14a667b..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/InlineMethodResolver.java +++ /dev/null @@ -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(); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java deleted file mode 100644 index 9edf37b1..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java +++ /dev/null @@ -1,3861 +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 org.jf.dexlib.Code.*; -import org.jf.dexlib.Code.Format.*; -import org.jf.dexlib.Util.AccessFlags; -import org.jf.dexlib.Util.ExceptionWithContext; -import org.jf.dexlib.Util.SparseArray; - -import java.util.BitSet; -import java.util.EnumSet; -import java.util.List; - -/** - * The MethodAnalyzer performs several functions. It "analyzes" the instructions and infers the register types - * for each register, it can deodex odexed instructions, and it can verify the bytecode. The analysis and verification - * are done in two separate passes, because the analysis has to process instructions multiple times in some cases, and - * there's no need to perform the verification multiple times, so we wait until the method is fully analyzed and then - * verify it. - * - * Before calling the analyze() method, you must have initialized the ClassPath by calling - * ClassPath.InitializeClassPath - */ -public class MethodAnalyzer { - private final ClassDataItem.EncodedMethod encodedMethod; - - private final DeodexUtil deodexUtil; - - private SparseArray instructions; - - private static final int NOT_ANALYZED = 0; - private static final int ANALYZED = 1; - private static final int VERIFIED = 2; - private int analyzerState = NOT_ANALYZED; - - private BitSet analyzedInstructions; - - private ValidationException validationException = null; - - //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the - //register types for this instruction to the parameter types, in order to have them propagate to all of its - //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first - //instruction, etc. - private AnalyzedInstruction startOfMethod; - - public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod, boolean deodex, - InlineMethodResolver inlineResolver) { - if (encodedMethod == null) { - throw new IllegalArgumentException("encodedMethod cannot be null"); - } - if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) { - throw new IllegalArgumentException("The method has no code"); - } - this.encodedMethod = encodedMethod; - - if (deodex) { - if (inlineResolver != null) { - this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile(), inlineResolver); - } else { - this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile()); - } - } else { - this.deodexUtil = null; - } - - //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't - //have to handle the case this special case of instruction being null, in the main class - startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) { - public boolean setsRegister() { - return false; - } - - @Override - public boolean setsWideRegister() { - return false; - } - - @Override - public boolean setsRegister(int registerNumber) { - return false; - } - - @Override - public int getDestinationRegister() { - assert false; - return -1; - }; - }; - - buildInstructionList(); - - analyzedInstructions = new BitSet(instructions.size()); - } - - public boolean isAnalyzed() { - return analyzerState >= ANALYZED; - } - - public boolean isVerified() { - return analyzerState == VERIFIED; - } - - public void analyze() { - assert encodedMethod != null; - assert encodedMethod.codeItem != null; - - if (analyzerState >= ANALYZED) { - //the instructions have already been analyzed, so there is nothing to do - return; - } - - CodeItem codeItem = encodedMethod.codeItem; - MethodIdItem methodIdItem = encodedMethod.method; - - int totalRegisters = codeItem.getRegisterCount(); - int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); - - int nonParameterRegisters = totalRegisters - parameterRegisters; - - for (AnalyzedInstruction instruction: instructions.getValues()) { - instruction.dead = true; - } - - //if this isn't a static method, determine which register is the "this" register and set the type to the - //current class - if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) { - nonParameterRegisters--; - int thisRegister = totalRegisters - parameterRegisters - 1; - - //if this is a constructor, then set the "this" register to an uninitialized reference of the current class - if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) { - setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, - RegisterType.getRegisterType(RegisterType.Category.UninitThis, - ClassPath.getClassDef(methodIdItem.getContainingClass()))); - } else { - setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, - RegisterType.getRegisterType(RegisterType.Category.Reference, - ClassPath.getClassDef(methodIdItem.getContainingClass()))); - } - } - - TypeListItem parameters = methodIdItem.getPrototype().getParameters(); - if (parameters != null) { - RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters); - for (int i=0; i=0; i=instructionsToAnalyze.nextSetBit(i+1)) { - instructionsToAnalyze.clear(i); - if (analyzedInstructions.get(i)) { - continue; - } - AnalyzedInstruction instructionToAnalyze = instructions.valueAt(i); - instructionToAnalyze.dead = false; - try { - if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) { - //if we had deodexed an odex instruction in a previous pass, we might have more specific - //register information now, so let's restore the original odexed instruction and - //re-deodex it - instructionToAnalyze.restoreOdexedInstruction(); - } - - if (!analyzeInstruction(instructionToAnalyze)) { - undeodexedInstructions.set(i); - continue; - } else { - didSomething = true; - undeodexedInstructions.clear(i); - } - } catch (ValidationException ex) { - this.validationException = ex; - int codeAddress = getInstructionAddress(instructionToAnalyze); - ex.setCodeAddress(codeAddress); - ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.opcode.name)); - ex.addContext(String.format("CodeAddress: %d", codeAddress)); - ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString())); - break; - } - - analyzedInstructions.set(instructionToAnalyze.getInstructionIndex()); - - for (AnalyzedInstruction successor: instructionToAnalyze.successors) { - instructionsToAnalyze.set(successor.getInstructionIndex()); - } - } - if (validationException != null) { - break; - } - } - - if (!didSomething) { - break; - } - - if (!undeodexedInstructions.isEmpty()) { - for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) { - instructionsToAnalyze.set(i); - } - } - } while (true); - - //Now, go through and fix up any unresolvable odex instructions. These are usually odex instructions - //that operate on a null register, and thus always throw an NPE. They can also be any sort of odex instruction - //that occurs after an unresolvable odex instruction. We deodex if possible, or replace with an - //UnresolvableOdexInstruction - for (int i=0; i=0; i=instructionsToVerify.nextSetBit(i+1)) { - instructionsToVerify.clear(i); - if (verifiedInstructions.get(i)) { - continue; - } - AnalyzedInstruction instructionToVerify = instructions.valueAt(i); - try { - verifyInstruction(instructionToVerify); - } catch (ValidationException ex) { - this.validationException = ex; - int codeAddress = getInstructionAddress(instructionToVerify); - ex.setCodeAddress(codeAddress); - ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name)); - ex.addContext(String.format("CodeAddress: %d", codeAddress)); - ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString())); - break; - } - - verifiedInstructions.set(instructionToVerify.getInstructionIndex()); - - for (AnalyzedInstruction successor: instructionToVerify.successors) { - instructionsToVerify.set(successor.getInstructionIndex()); - } - } - if (validationException != null) { - break; - } - } - - analyzerState = VERIFIED; - } - - private int getThisRegister() { - assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0; - - CodeItem codeItem = encodedMethod.codeItem; - assert codeItem != null; - - MethodIdItem methodIdItem = encodedMethod.method; - assert methodIdItem != null; - - int totalRegisters = codeItem.getRegisterCount(); - if (totalRegisters == 0) { - throw new ValidationException("A non-static method must have at least 1 register"); - } - - int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount(); - - return totalRegisters - parameterRegisters - 1; - } - - private boolean isInstanceConstructor() { - return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 && - (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; - } - - private boolean isStaticConstructor() { - return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 && - (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0; - } - - public AnalyzedInstruction getStartOfMethod() { - return startOfMethod; - } - - /** - * @return a read-only list containing the instructions for tihs method. - */ - public List getInstructions() { - return instructions.getValues(); - } - - public ClassDataItem.EncodedMethod getMethod() { - return this.encodedMethod; - } - - public ValidationException getValidationException() { - return validationException; - } - - private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) { - assert typeListItem != null; - assert parameterRegisterCount == typeListItem.getRegisterCount(); - - RegisterType[] registerTypes = new RegisterType[parameterRegisterCount]; - - int registerNum = 0; - for (TypeIdItem type: typeListItem.getTypes()) { - if (type.getRegisterCount() == 2) { - registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true); - registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false); - } else { - registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type); - } - } - - return registerTypes; - } - - public int getInstructionAddress(AnalyzedInstruction instruction) { - return instructions.keyAt(instruction.instructionIndex); - } - - private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, - RegisterType registerType) { - setPostRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(), - registerType); - } - - private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber, - RegisterType registerType) { - - BitSet changedInstructions = new BitSet(instructions.size()); - - if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) { - return; - } - - propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions); - - //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction - //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on - //the next iteration of the while loop. - //This could also be done recursively, but in large methods it would likely cause very deep recursion, - //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly - //annoying. - while (!changedInstructions.isEmpty()) { - for (int instructionIndex=changedInstructions.nextSetBit(0); - instructionIndex>=0; - instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) { - - changedInstructions.clear(instructionIndex); - - propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber, - changedInstructions); - } - } - - if (registerType.category == RegisterType.Category.LongLo) { - checkWidePair(registerNumber, analyzedInstruction); - setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, - RegisterType.getRegisterType(RegisterType.Category.LongHi, null)); - } else if (registerType.category == RegisterType.Category.DoubleLo) { - checkWidePair(registerNumber, analyzedInstruction); - setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, - RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null)); - } - } - - private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber, - BitSet changedInstructions) { - RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber); - for (AnalyzedInstruction successor: instruction.successors) { - if (successor.mergeRegister(registerNumber, postRegisterType, analyzedInstructions)) { - changedInstructions.set(successor.instructionIndex); - } - } - } - - private void buildInstructionList() { - assert encodedMethod != null; - assert encodedMethod.codeItem != null; - int registerCount = encodedMethod.codeItem.getRegisterCount(); - - Instruction[] insns = encodedMethod.codeItem.getInstructions(); - - instructions = new SparseArray(insns.length); - - //first, create all the instructions and populate the instructionAddresses array - int currentCodeAddress = 0; - for (int i=0; i currentCodeAddress); - - currentTry = tryItem; - - currentExceptionHandlers = buildExceptionHandlerArray(tryItem); - } - } - - //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers - //for the current instruction - if (currentTry != null && instructionOpcode.canThrow()) { - exceptionHandlers[i] = currentExceptionHandlers; - } - } - } - - //finally, populate the successors and predecessors for each instruction. We start at the fake "StartOfMethod" - //instruction and follow the execution path. Any unreachable code won't have any predecessors or successors, - //and no reachable code will have an unreachable predessor or successor - assert instructions.size() > 0; - BitSet instructionsToProcess = new BitSet(insns.length); - - addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers, instructionsToProcess); - while (!instructionsToProcess.isEmpty()) { - int currentInstructionIndex = instructionsToProcess.nextSetBit(0); - instructionsToProcess.clear(currentInstructionIndex); - - AnalyzedInstruction instruction = instructions.valueAt(currentInstructionIndex); - Opcode instructionOpcode = instruction.instruction.opcode; - int instructionCodeAddress = getInstructionAddress(instruction); - - if (instruction.instruction.opcode.canContinue()) { - if (instruction.instruction.opcode != Opcode.NOP || - !instruction.instruction.getFormat().variableSizeFormat) { - - if (currentInstructionIndex == instructions.size() - 1) { - throw new ValidationException("Execution can continue past the last instruction"); - } - - AnalyzedInstruction nextInstruction = instructions.valueAt(currentInstructionIndex+1); - addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess); - } - } - - if (instruction.instruction instanceof OffsetInstruction) { - OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction; - - if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) { - MultiOffsetInstruction switchDataInstruction = - (MultiOffsetInstruction)instructions.get(instructionCodeAddress + - offsetInstruction.getTargetAddressOffset()).instruction; - for (int targetAddressOffset: switchDataInstruction.getTargets()) { - AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + - targetAddressOffset); - - addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, - instructionsToProcess); - } - } else { - int targetAddressOffset = offsetInstruction.getTargetAddressOffset(); - AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress + - targetAddressOffset); - addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess); - } - } - } - } - - private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, - AnalyzedInstruction[][] exceptionHandlers, - BitSet instructionsToProcess) { - addPredecessorSuccessor(predecessor, successor, exceptionHandlers, instructionsToProcess, false); - } - - private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor, - AnalyzedInstruction[][] exceptionHandlers, - BitSet instructionsToProcess, boolean allowMoveException) { - - if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) { - throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name + - " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) + - " to the move-exception instruction at address 0x" + - Integer.toHexString(getInstructionAddress(successor))); - } - - if (!successor.addPredecessor(predecessor)) { - return; - } - - predecessor.addSuccessor(successor); - instructionsToProcess.set(successor.getInstructionIndex()); - - - //if the successor can throw an instruction, then we need to add the exception handlers as additional - //successors to the predecessor (and then apply this same logic recursively if needed) - //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually - //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other - //instruction. But since it doesn't modify any registers, we can treat it like any other instruction. - AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex]; - if (exceptionHandlersForSuccessor != null) { - //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction - //can throw an exception - assert successor.instruction.opcode.canThrow(); - - for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { - addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true); - } - } - } - - private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) { - int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length; - int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress(); - if (catchAllHandler != -1) { - exceptionHandlerCount++; - } - - AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount]; - for (int i=0; i Primitive32BitCategories = EnumSet.of( - RegisterType.Category.Null, - RegisterType.Category.One, - RegisterType.Category.Boolean, - RegisterType.Category.Byte, - RegisterType.Category.PosByte, - RegisterType.Category.Short, - RegisterType.Category.PosShort, - RegisterType.Category.Char, - RegisterType.Category.Integer, - RegisterType.Category.Float); - - private static final EnumSet WideLowCategories = EnumSet.of( - RegisterType.Category.LongLo, - RegisterType.Category.DoubleLo); - - private static final EnumSet WideHighCategories = EnumSet.of( - RegisterType.Category.LongHi, - RegisterType.Category.DoubleHi); - - private static final EnumSet ReferenceCategories = EnumSet.of( - RegisterType.Category.Null, - RegisterType.Category.Reference); - - private static final EnumSet ReferenceOrUninitThisCategories = EnumSet.of( - RegisterType.Category.Null, - RegisterType.Category.UninitThis, - RegisterType.Category.Reference); - - private static final EnumSet ReferenceOrUninitCategories = EnumSet.of( - RegisterType.Category.Null, - RegisterType.Category.UninitRef, - RegisterType.Category.UninitThis, - RegisterType.Category.Reference); - - private static final EnumSet ReferenceAndPrimitive32BitCategories = EnumSet.of( - RegisterType.Category.Null, - RegisterType.Category.One, - RegisterType.Category.Boolean, - RegisterType.Category.Byte, - RegisterType.Category.PosByte, - RegisterType.Category.Short, - RegisterType.Category.PosShort, - RegisterType.Category.Char, - RegisterType.Category.Integer, - RegisterType.Category.Float, - RegisterType.Category.Reference); - - private static final EnumSet BooleanCategories = EnumSet.of( - RegisterType.Category.Null, - RegisterType.Category.One, - RegisterType.Category.Boolean); - - private void analyzeMove(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType); - } - - private void verifyMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); - } - - private void analyzeMoveResult(AnalyzedInstruction analyzedInstruction) { - AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); - if (!previousInstruction.instruction.opcode.setsResult()) { - throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + - "invoke-*/fill-new-array instruction"); - } - - RegisterType resultRegisterType; - InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction; - Item item = invokeInstruction.getReferencedItem(); - - if (item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM) { - resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( - ((MethodIdItem)item).getPrototype().getReturnType()); - } else { - assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; - resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); - } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType); - } - - private void verifyMoveResult(AnalyzedInstruction analyzedInstruction, - EnumSet allowedCategories) { - if (analyzedInstruction.instructionIndex == 0) { - throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " + - "instruction in a method. It must occur after an invoke-*/fill-new-array instruction"); - } - - AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1); - - if (!previousInstruction.instruction.opcode.setsResult()) { - throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " + - "invoke-*/fill-new-array instruction"); - } - - //TODO: does dalvik allow a move-result after an invoke with a void return type? - RegisterType resultRegisterType; - - InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.getInstruction(); - Item item = invokeInstruction.getReferencedItem(); - - if (item instanceof MethodIdItem) { - resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem( - ((MethodIdItem)item).getPrototype().getReturnType()); - } else { - assert item instanceof TypeIdItem; - resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); - } - - if (!allowedCategories.contains(resultRegisterType.category)) { - throw new ValidationException(String.format("Wrong move-result* instruction for return value %s", - resultRegisterType.toString())); - } - } - - private void analyzeMoveException(AnalyzedInstruction analyzedInstruction) { - CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); - int instructionAddress = getInstructionAddress(analyzedInstruction); - - if (tries == null) { - throw new ValidationException("move-exception must be the first instruction in an exception handler block"); - } - - RegisterType exceptionType = null; - - for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { - if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { - exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, - ClassPath.getClassDef("Ljava/lang/Throwable;")); - break; - } - for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { - if (handler.getHandlerAddress() == instructionAddress) { - exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) - .merge(exceptionType); - } - } - } - - if (exceptionType == null) { - throw new ValidationException("move-exception must be the first instruction in an exception handler block"); - } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); - } - - private void verifyMoveException(AnalyzedInstruction analyzedInstruction) { - CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries(); - int instructionAddress = getInstructionAddress(analyzedInstruction); - - if (tries == null) { - throw new ValidationException("move-exception must be the first instruction in an exception handler block"); - } - - RegisterType exceptionType = null; - - for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) { - if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) { - exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference, - ClassPath.getClassDef("Ljava/lang/Throwable;")); - break; - } - for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) { - if (handler.getHandlerAddress() == instructionAddress) { - exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType) - .merge(exceptionType); - } - } - } - - if (exceptionType == null) { - throw new ValidationException("move-exception must be the first instruction in an exception handler block"); - } - - //TODO: check if the type is a throwable. Should we throw a ValidationException or print a warning? (does dalvik validate that it's a throwable? It doesn't in CodeVerify.c, but it might check in DexSwapVerify.c) - if (exceptionType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Exception type %s is not a reference type", - exceptionType.toString())); - } - } - - private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction) { - analyzeReturnVoidBarrier(analyzedInstruction, true); - } - - private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { - Instruction10x instruction = (Instruction10x)analyzedInstruction.instruction; - - Instruction10x deodexedInstruction = new Instruction10x(Opcode.RETURN_VOID); - - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - - if (analyzeResult) { - analyzeInstruction(analyzedInstruction); - } - } - - private void verifyReturnVoid(AnalyzedInstruction analyzedInstruction) { - TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); - if (returnType.getTypeDescriptor().charAt(0) != 'V') { - //TODO: could add which return-* variation should be used instead - throw new ValidationException("Cannot use return-void with a non-void return type (" + - returnType.getTypeDescriptor() + ")"); - } - } - - private void verifyReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { - /*if (this.isInstanceConstructor()) { - checkConstructorReturn(analyzedInstruction); - }*/ - - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - int returnRegister = instruction.getRegisterA(); - RegisterType returnRegisterType = getAndCheckSourceRegister(analyzedInstruction, returnRegister, - validCategories); - - TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType(); - if (returnType.getTypeDescriptor().charAt(0) == 'V') { - throw new ValidationException("Cannot use return with a void return type. Use return-void instead"); - } - - RegisterType methodReturnRegisterType = RegisterType.getRegisterTypeForTypeIdItem(returnType); - - if (!validCategories.contains(methodReturnRegisterType.category)) { - //TODO: could add which return-* variation should be used instead - throw new ValidationException(String.format("Cannot use %s with return type %s", - analyzedInstruction.instruction.opcode.name, returnType.getTypeDescriptor())); - } - - if (validCategories == ReferenceCategories) { - if (methodReturnRegisterType.type.isInterface()) { - if (returnRegisterType.category != RegisterType.Category.Null && - !returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) { - //TODO: how to handle warnings? - } - } else { - if (returnRegisterType.category == RegisterType.Category.Reference && - !returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) { - - throw new ValidationException(String.format("The return value in register v%d (%s) is not " + - "compatible with the method's return type %s", returnRegister, - returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType())); - } - } - } - } - - private void analyzeConst(AnalyzedInstruction analyzedInstruction) { - LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction; - - RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral()); - - //we assume that the literal value is a valid value for the given instruction type, because it's impossible - //to store an invalid literal with the instruction. so we don't need to check the type of the literal - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType); - } - - private void analyzeConstHigh16(AnalyzedInstruction analyzedInstruction) { - //the literal value stored in the instruction is a 16-bit value. When shifted left by 16, it will always be an - //integer - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Integer, null)); - } - - private void analyzeWideConst(AnalyzedInstruction analyzedInstruction) { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); - } - - private void analyzeConstString(AnalyzedInstruction analyzedInstruction) { - ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;"); - RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef); - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType); - } - - private void analyzeConstClass(AnalyzedInstruction analyzedInstruction) { - ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); - RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType); - } - - - private void verifyConstClass(AnalyzedInstruction analyzedInstruction) { - ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;"); - RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef); - - InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; - Item item = instruction.getReferencedItem(); - assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; - - //TODO: need to check class access - //make sure the referenced class is resolvable - ClassPath.getClassDef((TypeIdItem)item); - } - - private void verifyMonitor(AnalyzedInstruction analyzedInstruction) { - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceCategories); - } - - private void analyzeCheckCast(AnalyzedInstruction analyzedInstruction) { - InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; - - Item item = instruction.getReferencedItem(); - assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; - - RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType); - } - - private void verifyCheckCast(AnalyzedInstruction analyzedInstruction) { - { - //ensure the "source" register is a reference type - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - - RegisterType registerType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), - ReferenceCategories); - } - - { - //resolve and verify the class that we're casting to - InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; - - Item item = instruction.getReferencedItem(); - assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; - - //TODO: need to check class access - RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); - if (castRegisterType.category != RegisterType.Category.Reference) { - //TODO: verify that dalvik allows a non-reference type.. - //TODO: print a warning, but don't re-throw the exception. dalvik allows a non-reference type during validation (but throws an exception at runtime) - } - } - } - - private void analyzeInstanceOf(AnalyzedInstruction analyzedInstruction) { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Boolean, null)); - } - - private void verifyInstanceOf(AnalyzedInstruction analyzedInstruction) { - { - //ensure the register that is being checks is a reference type - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), ReferenceCategories); - } - - { - //resolve and verify the class that we're checking against - InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; - - Item item = instruction.getReferencedItem(); - assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; - RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); - if (registerType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use instance-of with a non-reference type %s", - registerType.toString())); - } - - //TODO: is it valid to use an array type? - //TODO: could probably do an even more sophisticated check, where we check the possible register types against the specified type. In some cases, we could determine that it always fails, and print a warning to that effect. - } - } - - private void analyzeArrayLength(AnalyzedInstruction analyzedInstruction) { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Integer, null)); - } - - private void verifyArrayLength(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - int arrayRegisterNumber = instruction.getRegisterB(); - RegisterType arrayRegisterType = getAndCheckSourceRegister(analyzedInstruction, arrayRegisterNumber, - ReferenceCategories); - - if (arrayRegisterType.type != null) { - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use array-length with non-array type %s", - arrayRegisterType.type.getClassType())); - } - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - } - } - - private void analyzeNewInstance(AnalyzedInstruction analyzedInstruction) { - InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; - - int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); - RegisterType destRegisterType = analyzedInstruction.getPostInstructionRegisterType(register); - if (destRegisterType.category != RegisterType.Category.Unknown) { - assert destRegisterType.category == RegisterType.Category.UninitRef; - - //the post-instruction destination register will only be set if we have already analyzed this instruction - //at least once. If this is the case, then the uninit reference has already been propagated to all - //successors and nothing else needs to be done. - return; - } - - Item item = instruction.getReferencedItem(); - assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM; - - RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item); - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getUnitializedReference(classType.type)); - } - - private void verifyNewInstance(AnalyzedInstruction analyzedInstruction) { - InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; - - int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); - RegisterType destRegisterType = analyzedInstruction.postRegisterMap[register]; - if (destRegisterType.category != RegisterType.Category.Unknown) { - assert destRegisterType.category == RegisterType.Category.UninitRef; - - //the "post-instruction" destination register will only be set if we've gone over - //this instruction at least once before. If this is the case, then we need to check - //all the other registers, and make sure that none of them contain the same - //uninitialized reference that is in the destination register. - - for (int i=0; i= 1<<16) { - throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " + - "is larger than the largest allowed register of v65535.", - instruction.getStartRegister(), - instruction.getStartRegister() + instruction.getRegCount() - 1)); - } - - verifyFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); - } - - private void verifyFillArrayData(AnalyzedInstruction analyzedInstruction) { - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - - int register = instruction.getRegisterA(); - RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); - assert registerType != null; - - if (registerType.category == RegisterType.Category.Null) { - return; - } - - if (registerType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " + - "type %s", register, registerType.toString())); - } - - assert registerType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type; - - if (arrayClassDef.getArrayDimensions() != 1) { - throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " + - "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); - } - - int elementWidth; - switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) { - case 'Z': - case 'B': - elementWidth = 1; - break; - case 'C': - case 'S': - elementWidth = 2; - break; - case 'I': - case 'F': - elementWidth = 4; - break; - case 'J': - case 'D': - elementWidth = 8; - break; - default: - throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " + - "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType())); - } - - - int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); - int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset; - AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress); - if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) { - throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x", - arrayDataCodeAddress)); - } - - ArrayDataPseudoInstruction arrayDataPseudoInstruction = - (ArrayDataPseudoInstruction)arrayDataInstruction.instruction; - - if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) { - throw new ValidationException(String.format("The array data at code address 0x%x does not have the " + - "correct element width for array type %s. Expecting element width %d, got element width %d.", - arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth, - arrayDataPseudoInstruction.getElementWidth())); - } - } - - private void verifyThrow(AnalyzedInstruction analyzedInstruction) { - int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); - - RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register); - assert registerType != null; - - if (registerType.category == RegisterType.Category.Null) { - return; - } - - if (registerType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d", - registerType.toString(), register)); - } - - assert registerType.type != null; - - if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) { - throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d", - registerType.type.getClassType(), register)); - } - } - - private void analyzeArrayDataOrSwitch(AnalyzedInstruction analyzedInstruction) { - int dataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); - - int dataCodeAddress = this.getInstructionAddress(analyzedInstruction) + dataAddressOffset; - AnalyzedInstruction dataAnalyzedInstruction = instructions.get(dataCodeAddress); - - if (dataAnalyzedInstruction != null) { - dataAnalyzedInstruction.dead = false; - - //if there is a preceding nop, it's deadness should be the same - AnalyzedInstruction priorInstruction = - instructions.valueAt(dataAnalyzedInstruction.getInstructionIndex()-1); - if (priorInstruction.getInstruction().opcode == Opcode.NOP && - !priorInstruction.getInstruction().getFormat().variableSizeFormat) { - - priorInstruction.dead = false; - } - } - } - - private void verifySwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) { - int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); - int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset(); - - getAndCheckSourceRegister(analyzedInstruction, register, Primitive32BitCategories); - - int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset; - AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress); - - if (switchDataAnalyzedInstruction == null || - switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) { - throw new ValidationException(String.format("There is no %s structure at code address 0x%x", - expectedSwitchDataFormat.name(), switchDataCodeAddress)); - } - } - - private void analyzeFloatWideCmp(AnalyzedInstruction analyzedInstruction) { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Byte, null)); - } - - private void verifyFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories); - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories); - } - - private void verifyIfEqNe(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); - assert registerType1 != null; - - RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert registerType2 != null; - - if (!( - (ReferenceCategories.contains(registerType1.category) && - ReferenceCategories.contains(registerType2.category)) - || - (Primitive32BitCategories.contains(registerType1.category) && - Primitive32BitCategories.contains(registerType2.category)) - )) { - - throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " + - "%s. They must both be a reference type or a primitive 32 bit type.", - analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString())); - } - } - - private void verifyIf(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); - } - - private void verifyIfEqzNez(AnalyzedInstruction analyzedInstruction) { - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), - ReferenceAndPrimitive32BitCategories); - } - - private void verifyIfz(AnalyzedInstruction analyzedInstruction) { - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories); - } - - private void analyze32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(instructionCategory, null)); - } - - private void verify32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); - - RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert arrayRegisterType != null; - - if (arrayRegisterType.category != RegisterType.Category.Null) { - if (arrayRegisterType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use %s with non-array type %s", - analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); - } - - assert arrayRegisterType.type != null; - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use %s with non-array type %s", - analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); - } - - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; - - if (arrayClassDef.getArrayDimensions() != 1) { - throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", - analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); - } - - RegisterType arrayBaseType = - RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); - if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { - throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - arrayRegisterType.type.getClassType())); - } - } - } - - private void analyzeAgetWide(AnalyzedInstruction analyzedInstruction) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert arrayRegisterType != null; - - if (arrayRegisterType.category != RegisterType.Category.Null) { - assert arrayRegisterType.type != null; - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", - arrayRegisterType.type.getClassType())); - } - - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; - - char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); - if (arrayBaseType == 'J') { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); - } else if (arrayBaseType == 'D') { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null)); - } else { - throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + - "array type for the instruction.", arrayRegisterType.type.getClassType())); - } - } else { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.LongLo, null)); - } - } - - private void verifyAgetWide(AnalyzedInstruction analyzedInstruction) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); - - RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert arrayRegisterType != null; - - if (arrayRegisterType.category != RegisterType.Category.Null) { - if (arrayRegisterType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", - arrayRegisterType.category.toString())); - } - - assert arrayRegisterType.type != null; - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s", - arrayRegisterType.type.getClassType())); - } - - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; - - if (arrayClassDef.getArrayDimensions() != 1) { - throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s", - arrayRegisterType.type.getClassType())); - } - - char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); - if (arrayBaseType != 'J' && arrayBaseType != 'D') { - throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " + - "array type for the instruction.", arrayRegisterType.type.getClassType())); - } - } - } - - private void analyzeAgetObject(AnalyzedInstruction analyzedInstruction) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert arrayRegisterType != null; - - if (arrayRegisterType.category != RegisterType.Category.Null) { - assert arrayRegisterType.type != null; - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", - arrayRegisterType.type.getClassType())); - } - - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; - - ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); - char elementTypePrefix = elementClassDef.getClassType().charAt(0); - if (elementTypePrefix != 'L' && elementTypePrefix != '[') { - throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + - "array type for the instruction.", arrayRegisterType.type.getClassType())); - } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef)); - } else { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(RegisterType.Category.Null, null)); - } - } - - private void verifyAgetObject(AnalyzedInstruction analyzedInstruction) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); - - RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert arrayRegisterType != null; - - if (arrayRegisterType.category != RegisterType.Category.Null) { - if (arrayRegisterType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", - arrayRegisterType.category.toString())); - } - - assert arrayRegisterType.type != null; - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", - arrayRegisterType.type.getClassType())); - } - - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; - - ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); - char elementTypePrefix = elementClassDef.getClassType().charAt(0); - if (elementTypePrefix != 'L' && elementTypePrefix != '[') { - throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + - "array type for the instruction.", arrayRegisterType.type.getClassType())); - } - } - } - - private void verify32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); - - RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); - assert sourceRegisterType != null; - RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); - if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { - throw new ValidationException(String.format("Cannot use %s with source register type %s.", - analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); - } - - - RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert arrayRegisterType != null; - - if (arrayRegisterType.category != RegisterType.Category.Null) { - if (arrayRegisterType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use %s with non-array type %s", - analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString())); - } - - assert arrayRegisterType.type != null; - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use %s with non-array type %s", - analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); - } - - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; - - if (arrayClassDef.getArrayDimensions() != 1) { - throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s", - analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType())); - } - - RegisterType arrayBaseType = - RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType()); - if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) { - throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - arrayRegisterType.type.getClassType())); - } - } - } - - private void verifyAputWide(AnalyzedInstruction analyzedInstruction) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); - - RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert arrayRegisterType != null; - - if (arrayRegisterType.category != RegisterType.Category.Null) { - if (arrayRegisterType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", - arrayRegisterType.category.toString())); - } - - assert arrayRegisterType.type != null; - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s", - arrayRegisterType.type.getClassType())); - } - - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; - - if (arrayClassDef.getArrayDimensions() != 1) { - throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s", - arrayRegisterType.type.getClassType())); - } - - char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0); - if (arrayBaseType != 'J' && arrayBaseType != 'D') { - throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " + - "array type for the instruction.", arrayRegisterType.type.getClassType())); - } - } - } - - private void verifyAputObject(AnalyzedInstruction analyzedInstruction) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories); - - RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); - assert sourceRegisterType != null; - - //TODO: ensure sourceRegisterType is a Reference type? - - RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - assert arrayRegisterType != null; - - if (arrayRegisterType.category != RegisterType.Category.Null) { - //don't check the source type against the array type, just make sure it is an array of reference types - - if (arrayRegisterType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", - arrayRegisterType.category.toString())); - } - - assert arrayRegisterType.type != null; - if (arrayRegisterType.type.getClassType().charAt(0) != '[') { - throw new ValidationException(String.format("Cannot use aget-object with non-array type %s", - arrayRegisterType.type.getClassType())); - } - - assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef; - ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type; - - ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass(); - char elementTypePrefix = elementClassDef.getClassType().charAt(0); - if (elementTypePrefix != 'L' && elementTypePrefix != '[') { - throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " + - "array type for the instruction.", arrayRegisterType.type.getClassType())); - } - } - } - - private void analyze32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(instructionCategory, null)); - } - - private void verify32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - ReferenceOrUninitThisCategories); - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - if (objectRegisterType.category != RegisterType.Category.Null && - !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { - throw new ValidationException(String.format("Cannot access field %s through type %s", - field.getFieldString(), objectRegisterType.type.getClassType())); - } - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void analyzeIgetWideObject(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); - } - - private void verifyIgetWide(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - ReferenceOrUninitThisCategories); - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - if (objectRegisterType.category != RegisterType.Category.Null && - !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { - throw new ValidationException(String.format("Cannot access field %s through type %s", - field.getFieldString(), objectRegisterType.type.getClassType())); - } - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (!WideLowCategories.contains(fieldType.category)) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void verifyIgetObject(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - ReferenceOrUninitThisCategories); - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - if (objectRegisterType.category != RegisterType.Category.Null && - !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { - throw new ValidationException(String.format("Cannot access field %s through type %s", - field.getFieldString(), objectRegisterType.type.getClassType())); - } - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (fieldType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void verify32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - ReferenceOrUninitThisCategories); - - RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); - assert sourceRegisterType != null; - - //per CodeVerify.c in dalvik: - //java generates synthetic functions that write byte values into boolean fields - if (sourceRegisterType.category == RegisterType.Category.Byte && - instructionCategory == RegisterType.Category.Boolean) { - - sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null); - } - - RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); - if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { - throw new ValidationException(String.format("Cannot use %s with source register type %s.", - analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); - } - - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - if (objectRegisterType.category != RegisterType.Category.Null && - !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { - throw new ValidationException(String.format("Cannot access field %s through type %s", - field.getFieldString(), objectRegisterType.type.getClassType())); - } - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void verifyIputWide(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - ReferenceOrUninitThisCategories); - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - if (objectRegisterType.category != RegisterType.Category.Null && - !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { - throw new ValidationException(String.format("Cannot access field %s through type %s", - field.getFieldString(), objectRegisterType.type.getClassType())); - } - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (!WideLowCategories.contains(fieldType.category)) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void verifyIputObject(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - ReferenceOrUninitThisCategories); - - RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), - ReferenceCategories); - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - if (objectRegisterType.category != RegisterType.Category.Null && - !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) { - throw new ValidationException(String.format("Cannot access field %s through type %s", - field.getFieldString(), objectRegisterType.type.getClassType())); - } - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (fieldType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - - if (sourceRegisterType.category != RegisterType.Category.Null && - !fieldType.type.isInterface() && - !sourceRegisterType.type.extendsClass(fieldType.type)) { - - throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s", - sourceRegisterType.type.getClassType(), fieldType.type.getClassType())); - } - } - - private void analyze32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(instructionCategory, null)); - } - - private void verify32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void analyzeSgetWideObject(AnalyzedInstruction analyzedInstruction) { - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); - } - - private void verifySgetWide(AnalyzedInstruction analyzedInstruction) { - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - - if (fieldType.category != RegisterType.Category.LongLo && - fieldType.category != RegisterType.Category.DoubleLo) { - - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void verifySgetObject(AnalyzedInstruction analyzedInstruction) { - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (fieldType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void verify32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction, - RegisterType.Category instructionCategory) { - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - - RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); - assert sourceRegisterType != null; - - //per CodeVerify.c in dalvik: - //java generates synthetic functions that write byte values into boolean fields - if (sourceRegisterType.category == RegisterType.Category.Byte && - instructionCategory == RegisterType.Category.Boolean) { - - sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null); - } - - RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null); - if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) { - throw new ValidationException(String.format("Cannot use %s with source register type %s.", - analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString())); - } - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void verifySputWide(AnalyzedInstruction analyzedInstruction) { - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories); - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (!WideLowCategories.contains(fieldType.category)) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - } - - private void verifySputObject(AnalyzedInstruction analyzedInstruction) { - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - - RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), - ReferenceCategories); - - //TODO: check access - Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem(); - assert referencedItem instanceof FieldIdItem; - FieldIdItem field = (FieldIdItem)referencedItem; - - RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType()); - - if (fieldType.category != RegisterType.Category.Reference) { - throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " + - "for the instruction.", analyzedInstruction.instruction.opcode.name, - field.getFieldString())); - } - - if (sourceRegisterType.category != RegisterType.Category.Null && - !fieldType.type.isInterface() && - !sourceRegisterType.type.extendsClass(fieldType.type)) { - - throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s", - sourceRegisterType.type.getClassType(), fieldType.type.getClassType())); - } - } - - private void analyzeInvokeDirect(AnalyzedInstruction analyzedInstruction) { - FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; - analyzeInvokeDirectCommon(analyzedInstruction, new Format35cRegisterIterator(instruction)); - } - - private void verifyInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) { - FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; - verifyInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction)); - } - - private void analyzeInvokeDirectRange(AnalyzedInstruction analyzedInstruction) { - RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; - analyzeInvokeDirectCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction)); - } - - private void verifyInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) { - RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; - verifyInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction)); - } - - private static final int INVOKE_VIRTUAL = 0x01; - private static final int INVOKE_SUPER = 0x02; - private static final int INVOKE_DIRECT = 0x04; - private static final int INVOKE_INTERFACE = 0x08; - private static final int INVOKE_STATIC = 0x10; - - private void analyzeInvokeDirectCommon(AnalyzedInstruction analyzedInstruction, RegisterIterator registers) { - //the only time that an invoke instruction changes a register type is when using invoke-direct on a - //constructor () method, which changes the uninitialized reference (and any register that the same - //uninit reference has been copied to) to an initialized reference - - InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction; - - Item item = instruction.getReferencedItem(); - assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; - MethodIdItem methodIdItem = (MethodIdItem)item; - - if (!methodIdItem.getMethodName().getStringValue().equals("")) { - return; - } - - RegisterType objectRegisterType; - //the object register is always the first register - int objectRegister = registers.getRegister(); - - objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister); - assert objectRegisterType != null; - - if (objectRegisterType.category != RegisterType.Category.UninitRef && - objectRegisterType.category != RegisterType.Category.UninitThis) { - return; - } - - setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister, - RegisterType.getRegisterType(RegisterType.Category.Reference, objectRegisterType.type)); - - for (int i=0; i method %s on uninitialized " + - "reference type %s", methodIdItem.getMethodString(), - objectRegisterType.type.getClassType())); - } - } else if (objectRegisterType.category == RegisterType.Category.Reference) { - if (isInit) { - throw new ValidationException(String.format("Cannot invoke %s on initialized reference type %s", - methodIdItem.getMethodString(), objectRegisterType.type.getClassType())); - } - } else if (objectRegisterType.category == RegisterType.Category.Null) { - if (isInit) { - throw new ValidationException(String.format("Cannot invoke %s on a null reference", - methodIdItem.getMethodString())); - } - } - else { - throw new ValidationException(String.format("Cannot invoke %s on non-reference type %s", - methodIdItem.getMethodString(), objectRegisterType.toString())); - } - - if (isInit) { - if (objectRegisterType.type.getSuperclass() == methodClassDef) { - if (!encodedMethod.method.getMethodName().getStringValue().equals("")) { - throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " + - "match the method type exactly", methodIdItem.getMethodString(), - objectRegisterType.type.getClassType())); - } - } - } - - if ((invokeType & INVOKE_INTERFACE) == 0 && objectRegisterType.category != RegisterType.Category.Null && - !objectRegisterType.type.extendsClass(methodClassDef)) { - - throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " + - "does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(), - methodClassDef.getClassType())); - } - } - - if (typeListItem != null) { - List parameterTypes = typeListItem.getTypes(); - int parameterTypeIndex = 0; - while (!registers.pastEnd()) { - assert parameterTypeIndex < parameterTypes.size(); - RegisterType parameterType = - RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex)); - - int register = registers.getRegister(); - - RegisterType parameterRegisterType; - if (WideLowCategories.contains(parameterType.category)) { - parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories); - - if (!registers.moveNext()) { - throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d", - parameterTypeIndex+1)); - } - int nextRegister = registers.getRegister(); - - if (nextRegister != register + 1) { - throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " + - "must be consecutive.", register, nextRegister)); - } - } else { - parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register); - } - - assert parameterRegisterType != null; - - if (!parameterRegisterType.canBeAssignedTo(parameterType)) { - throw new ValidationException( - String.format("Invalid register type %s for parameter %d %s.", - parameterRegisterType.toString(), parameterTypeIndex+1, - parameterType.toString())); - } - - parameterTypeIndex++; - registers.moveNext(); - } - } - } - - private void analyzeUnaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory) { - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(destRegisterCategory, null)); - } - - private void verifyUnaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSourceCategories) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSourceCategories); - } - - private void analyzeBinaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory, - boolean checkForBoolean) { - if (checkForBoolean) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - RegisterType source1RegisterType = - analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - RegisterType source2RegisterType = - analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC()); - - if (BooleanCategories.contains(source1RegisterType.category) && - BooleanCategories.contains(source2RegisterType.category)) { - - destRegisterCategory = RegisterType.Category.Boolean; - } - } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(destRegisterCategory, null)); - } - - private void verifyBinaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories, - EnumSet validSource2Categories) { - ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource1Categories); - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validSource2Categories); - } - - private void analyzeBinary2AddrOp(AnalyzedInstruction analyzedInstruction, - RegisterType.Category destRegisterCategory, boolean checkForBoolean) { - if (checkForBoolean) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType source1RegisterType = - analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA()); - RegisterType source2RegisterType = - analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - - if (BooleanCategories.contains(source1RegisterType.category) && - BooleanCategories.contains(source2RegisterType.category)) { - - destRegisterCategory = RegisterType.Category.Boolean; - } - } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(destRegisterCategory, null)); - } - - private void verifyBinary2AddrOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories, - EnumSet validSource2Categories) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), validSource1Categories); - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource2Categories); - } - - private void analyzeLiteralBinaryOp(AnalyzedInstruction analyzedInstruction, - RegisterType.Category destRegisterCategory, boolean checkForBoolean) { - if (checkForBoolean) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType sourceRegisterType = - analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); - - if (BooleanCategories.contains(sourceRegisterType.category)) { - long literal = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral(); - if (literal == 0 || literal == 1) { - destRegisterCategory = RegisterType.Category.Boolean; - } - } - } - - setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, - RegisterType.getRegisterType(destRegisterCategory, null)); - } - - private void verifyLiteralBinaryOp(AnalyzedInstruction analyzedInstruction) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories); - } - - private RegisterType.Category getDestTypeForLiteralShiftRight(AnalyzedInstruction analyzedInstruction, - boolean signedShift) { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - Primitive32BitCategories); - long literalShift = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral(); - - if (literalShift == 0) { - return sourceRegisterType.category; - } - - RegisterType.Category destRegisterCategory; - if (!signedShift) { - destRegisterCategory = RegisterType.Category.Integer; - } else { - destRegisterCategory = sourceRegisterType.category; - } - - if (literalShift >= 32) { - //TODO: add warning - return destRegisterCategory; - } - - switch (sourceRegisterType.category) { - case Integer: - case Float: - if (!signedShift) { - if (literalShift > 24) { - return RegisterType.Category.PosByte; - } - if (literalShift >= 16) { - return RegisterType.Category.Char; - } - } else { - if (literalShift >= 24) { - return RegisterType.Category.Byte; - } - if (literalShift >= 16) { - return RegisterType.Category.Short; - } - } - break; - case Short: - if (signedShift && literalShift >= 8) { - return RegisterType.Category.Byte; - } - break; - case PosShort: - if (literalShift >= 8) { - return RegisterType.Category.PosByte; - } - break; - case Char: - if (literalShift > 8) { - return RegisterType.Category.PosByte; - } - break; - case Byte: - break; - case PosByte: - return RegisterType.Category.PosByte; - case Null: - case One: - case Boolean: - return RegisterType.Category.Null; - default: - assert false; - } - - return destRegisterCategory; - } - - - private void analyzeExecuteInline(AnalyzedInstruction analyzedInstruction) { - if (deodexUtil == null) { - throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing"); - } - - Instruction35mi instruction = (Instruction35mi)analyzedInstruction.instruction; - - DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction); - MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(deodexUtil); - if (inlineMethodIdItem == null) { - throw new ValidationException(String.format("Cannot load inline method with index %d", - instruction.getInlineIndex())); - } - - Opcode deodexedOpcode = null; - switch (inlineMethod.methodType) { - case DeodexUtil.Direct: - deodexedOpcode = Opcode.INVOKE_DIRECT; - break; - case DeodexUtil.Static: - deodexedOpcode = Opcode.INVOKE_STATIC; - break; - case DeodexUtil.Virtual: - deodexedOpcode = Opcode.INVOKE_VIRTUAL; - break; - default: - assert false; - } - - Instruction35c deodexedInstruction = new Instruction35c(deodexedOpcode, instruction.getRegCount(), - instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), - instruction.getRegisterG(), instruction.getRegisterA(), inlineMethodIdItem); - - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - - analyzeInstruction(analyzedInstruction); - } - - private void analyzeExecuteInlineRange(AnalyzedInstruction analyzedInstruction) { - if (deodexUtil == null) { - throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing"); - } - - Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction; - - DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction); - MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem(deodexUtil); - if (inlineMethodIdItem == null) { - throw new ValidationException(String.format("Cannot load inline method with index %d", - instruction.getInlineIndex())); - } - - Opcode deodexedOpcode = null; - switch (inlineMethod.methodType) { - case DeodexUtil.Direct: - deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE; - break; - case DeodexUtil.Static: - deodexedOpcode = Opcode.INVOKE_STATIC_RANGE; - break; - case DeodexUtil.Virtual: - deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE; - break; - default: - assert false; - } - - Instruction3rc deodexedInstruction = new Instruction3rc(deodexedOpcode, (short)instruction.getRegCount(), - instruction.getStartRegister(), inlineMethodIdItem); - - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - - analyzeInstruction(analyzedInstruction); - } - - private void analyzeInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction) { - analyzeInvokeDirectEmpty(analyzedInstruction, true); - } - - private void analyzeInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { - Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction; - - Instruction35c deodexedInstruction = new Instruction35c(Opcode.INVOKE_DIRECT, instruction.getRegCount(), - instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), - instruction.getRegisterG(), instruction.getRegisterA(), instruction.getReferencedItem()); - - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - - if (analyzeResult) { - analyzeInstruction(analyzedInstruction); - } - } - - private void analyzeInvokeObjectInitRange(AnalyzedInstruction analyzedInstruction) { - analyzeInvokeObjectInitRange(analyzedInstruction, true); - } - - private void analyzeInvokeObjectInitRange(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { - Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction; - - Instruction3rc deodexedInstruction = new Instruction3rc(Opcode.INVOKE_DIRECT_RANGE, - (short)instruction.getRegCount(), instruction.getStartRegister(), instruction.getReferencedItem()); - - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - - if (analyzeResult) { - analyzeInstruction(analyzedInstruction); - } - } - - private boolean analyzeIputIgetQuick(AnalyzedInstruction analyzedInstruction) { - Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction; - - int fieldOffset = instruction.getFieldOffset(); - RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), - ReferenceOrUninitCategories); - - if (objectRegisterType.category == RegisterType.Category.Null) { - return false; - } - - ClassPath.ClassDef accessingClass = - ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false); - if (accessingClass == null) { - throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s", - this.encodedMethod.method.getContainingClass())); - } - - FieldIdItem fieldIdItem = deodexUtil.lookupField(accessingClass, objectRegisterType.type, fieldOffset); - if (fieldIdItem == null) { - throw new ValidationException(String.format("Could not resolve the field in class %s at offset %d", - objectRegisterType.type.getClassType(), fieldOffset)); - } - - String fieldType = fieldIdItem.getFieldType().getTypeDescriptor(); - - Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, instruction.opcode); - - Instruction22c deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(), - (byte)instruction.getRegisterB(), fieldIdItem); - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - - analyzeInstruction(analyzedInstruction); - - return true; - } - - private boolean analyzeInvokeVirtualQuick(AnalyzedInstruction analyzedInstruction, boolean isSuper, - boolean isRange) { - int methodIndex; - int objectRegister; - - - if (isRange) { - Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; - methodIndex = instruction.getVtableIndex(); - objectRegister = instruction.getStartRegister(); - } else { - Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; - methodIndex = instruction.getVtableIndex(); - objectRegister = instruction.getRegisterD(); - } - - RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister, - ReferenceOrUninitCategories); - - if (objectRegisterType.category == RegisterType.Category.Null) { - return false; - } - - MethodIdItem methodIdItem = null; - ClassPath.ClassDef accessingClass = - ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false); - if (accessingClass == null) { - throw new ExceptionWithContext(String.format("Could not find ClassDef for current class: %s", - this.encodedMethod.method.getContainingClass())); - } - if (isSuper) { - if (accessingClass.getSuperclass() != null) { - methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass.getSuperclass(), - methodIndex); - } - - if (methodIdItem == null) { - //it's possible that the pre-odexed instruction had used the method from the current class instead - //of from the superclass (although the superclass method is still what would actually be called). - //And so the MethodIdItem for the superclass method may not be in the dex file. Let's try to get the - //MethodIdItem for the method in the current class instead - methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, accessingClass, methodIndex); - } - } else{ - methodIdItem = deodexUtil.lookupVirtualMethod(accessingClass, objectRegisterType.type, methodIndex); - } - - if (methodIdItem == null) { - throw new ValidationException(String.format("Could not resolve the method in class %s at index %d", - objectRegisterType.type.getClassType(), methodIndex)); - } - - - Instruction deodexedInstruction; - if (isRange) { - Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; - Opcode opcode; - if (isSuper) { - opcode = Opcode.INVOKE_SUPER_RANGE; - } else { - opcode = Opcode.INVOKE_VIRTUAL_RANGE; - } - - deodexedInstruction = new Instruction3rc(opcode, (short)instruction.getRegCount(), - instruction.getStartRegister(), methodIdItem); - } else { - Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; - Opcode opcode; - if (isSuper) { - opcode = Opcode.INVOKE_SUPER; - } else { - opcode = Opcode.INVOKE_VIRTUAL; - } - - deodexedInstruction = new Instruction35c(opcode, instruction.getRegCount(), - instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(), - instruction.getRegisterG(), instruction.getRegisterA(), methodIdItem); - } - - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - analyzeInstruction(analyzedInstruction); - - return true; - } - - private boolean analyzePutGetVolatile(AnalyzedInstruction analyzedInstruction) { - return analyzePutGetVolatile(analyzedInstruction, true); - } - - private boolean analyzePutGetVolatile(AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { - FieldIdItem fieldIdItem = - (FieldIdItem)(((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem()); - - String fieldType = fieldIdItem.getFieldType().getTypeDescriptor(); - - Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, - analyzedInstruction.instruction.opcode); - - Instruction deodexedInstruction; - - if (analyzedInstruction.instruction.opcode.isOdexedStaticVolatile()) { - SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction; - if (analyzedInstruction.instruction.opcode.format == Format.Format21c) { - deodexedInstruction = new Instruction21c(opcode, (byte)instruction.getRegisterA(), fieldIdItem); - } else { - assert(analyzedInstruction.instruction.opcode.format == Format.Format41c); - deodexedInstruction = new Instruction41c(opcode, (byte)instruction.getRegisterA(), fieldIdItem); - } - } else { - TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; - - if (analyzedInstruction.instruction.opcode.format == Format.Format22c) { - deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(), - (byte)instruction.getRegisterB(), fieldIdItem); - } else { - assert(analyzedInstruction.instruction.opcode.format == Format.Format52c); - deodexedInstruction = new Instruction52c(opcode, (byte)instruction.getRegisterA(), - (byte)instruction.getRegisterB(), fieldIdItem); - } - } - - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - - if (analyzeResult) { - analyzeInstruction(analyzedInstruction); - } - return true; - } - - private void analyzeInvokeObjectInitJumbo(AnalyzedInstruction analyzedInstruction) { - Instruction5rc instruction = (Instruction5rc)analyzedInstruction.instruction; - - Instruction5rc deodexedInstruction = new Instruction5rc(Opcode.INVOKE_DIRECT_JUMBO, - instruction.getRegCount(), instruction.getStartRegister(), instruction.getReferencedItem()); - - analyzedInstruction.setDeodexedInstruction(deodexedInstruction); - - analyzeInstruction(analyzedInstruction); - } - - private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory, - RegisterType.Category instructionCategory) { - if (arrayFieldCategory == instructionCategory) { - return true; - } - - if ((arrayFieldCategory == RegisterType.Category.Integer && - instructionCategory == RegisterType.Category.Float) || - (arrayFieldCategory == RegisterType.Category.Float && - instructionCategory == RegisterType.Category.Integer)) { - return true; - } - return false; - } - - private static RegisterType getAndCheckSourceRegister(AnalyzedInstruction analyzedInstruction, int registerNumber, - EnumSet validCategories) { - assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length; - - RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber); - assert registerType != null; - - checkRegister(registerType, registerNumber, validCategories); - - if (validCategories == WideLowCategories) { - checkRegister(registerType, registerNumber, WideLowCategories); - checkWidePair(registerNumber, analyzedInstruction); - - RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1); - assert secondRegisterType != null; - checkRegister(secondRegisterType, registerNumber+1, WideHighCategories); - } - - return registerType; - } - - private static void checkRegister(RegisterType registerType, int registerNumber, EnumSet validCategories) { - if (!validCategories.contains(registerType.category)) { - throw new ValidationException(String.format("Invalid register type %s for register v%d.", - registerType.toString(), registerNumber)); - } - } - - private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) { - if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) { - throw new ValidationException(String.format("v%d cannot be used as the first register in a wide register" + - "pair because it is the last register.", registerNumber)); - } - } - - private static interface RegisterIterator { - int getRegister(); - boolean moveNext(); - int getCount(); - boolean pastEnd(); - } - - private static class Format35cRegisterIterator implements RegisterIterator { - private final int registerCount; - private final int[] registers; - private int currentRegister = 0; - - public Format35cRegisterIterator(FiveRegisterInstruction instruction) { - registerCount = instruction.getRegCount(); - registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(), - instruction.getRegisterF(), instruction.getRegisterG(), - instruction.getRegisterA()}; - } - - public int getRegister() { - return registers[currentRegister]; - } - - public boolean moveNext() { - currentRegister++; - return !pastEnd(); - } - - public int getCount() { - return registerCount; - } - - public boolean pastEnd() { - return currentRegister >= registerCount; - } - } - - private static class Format3rcRegisterIterator implements RegisterIterator { - private final int startRegister; - private final int registerCount; - private int currentRegister = 0; - - public Format3rcRegisterIterator(RegisterRangeInstruction instruction) { - startRegister = instruction.getStartRegister(); - registerCount = instruction.getRegCount(); - } - - public int getRegister() { - return startRegister + currentRegister; - } - - public boolean moveNext() { - currentRegister++; - return !pastEnd(); - } - - public int getCount() { - return registerCount; - } - - public boolean pastEnd() { - return currentRegister >= registerCount; - } - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java deleted file mode 100644 index ed67732a..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/RegisterType.java +++ /dev/null @@ -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 internedRegisterTypes = - new HashMap(); - - 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 is called - UninitRef, - //the UninitThis category is used the "this" register inside an method, before the superclass' - //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 - // 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; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java deleted file mode 100644 index 5ad98fe0..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/SyntheticAccessorResolver.java +++ /dev/null @@ -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 resolvedAccessors = new HashMap(); - - 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; - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java deleted file mode 100644 index 32c13cb4..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ValidationException.java +++ /dev/null @@ -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; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java deleted file mode 100644 index b9ff0661..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/FiveRegisterInstruction.java +++ /dev/null @@ -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(); -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java deleted file mode 100644 index 5de88f40..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java +++ /dev/null @@ -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 getElements() { - return new Iterator() { - final int elementCount = getElementCount(); - int i=0; - int position=0; - final ArrayElement arrayElement = new ArrayElement(encodedValues, getElementWidth()); - - public boolean hasNext() { - return i 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); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java deleted file mode 100644 index 46ea9686..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java deleted file mode 100644 index 7fae04c3..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java deleted file mode 100644 index ca30c7b4..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java +++ /dev/null @@ -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); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java deleted file mode 100644 index 6d8d774d..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java +++ /dev/null @@ -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); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java deleted file mode 100644 index 75af9df7..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20bc.java +++ /dev/null @@ -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); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java deleted file mode 100644 index f970cfb5..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java +++ /dev/null @@ -1,94 +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; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction20t extends OffsetInstruction { - public static final InstructionFactory Factory = new Factory(); - private int targetAddressOffset; - - public Instruction20t(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 Instruction20t(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - assert buffer[bufferIndex] == opcode.value; - - this.targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex+2); - 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 < -32768 || targetAddressOffset > 32767) { - throw new RuntimeException("The address offset is out of range. It must be in [-32768,-1] or [1, 32768]"); - } - - out.writeByte(opcode.value); - out.writeByte(0x00); - out.writeShort(targetAddressOffset); - } - - public void updateTargetAddressOffset(int targetAddressOffset) { - this.targetAddressOffset = targetAddressOffset; - } - - public Format getFormat() { - return Format.Format20t; - } - - public int getTargetAddressOffset() { - return targetAddressOffset; - } - - private static class Factory implements InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction20t(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java deleted file mode 100644 index 2c12a9b8..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java +++ /dev/null @@ -1,115 +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.InstructionWithReference; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.SingleRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Item; -import org.jf.dexlib.TypeIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; - -public class Instruction21c extends InstructionWithReference implements SingleRegisterInstruction, - InstructionWithJumboVariant { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - - public Instruction21c(Opcode opcode, short regA, Item referencedItem) { - super(opcode, referencedItem); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - if (opcode == Opcode.NEW_INSTANCE) { - assert referencedItem instanceof TypeIdItem; - if (((TypeIdItem)referencedItem).getTypeDescriptor().charAt(0) != 'L') { - throw new RuntimeException("Only class references can be used with the new-instance opcode"); - } - } - - this.regA = (byte)regA; - } - - private Instruction21c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - super(dexFile, opcode, buffer, bufferIndex); - - if (opcode == Opcode.NEW_INSTANCE && - ((TypeIdItem)this.getReferencedItem()).getTypeDescriptor().charAt(0) != 'L') { - - throw new RuntimeException("Only class references can be used with the new-instance opcode"); - } - - this.regA = buffer[bufferIndex + 1]; - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - if(getReferencedItem().getIndex() > 0xFFFF) { - if (opcode.hasJumboOpcode()) { - throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", - opcode.referenceType.name(), opcode.getJumboOpcode().name)); - } else { - throw new RuntimeException(String.format("%s index is too large", opcode.referenceType.name())); - } - } - - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeShort(getReferencedItem().getIndex()); - } - - public Format getFormat() { - return Format.Format21c; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - public Instruction makeJumbo() { - Opcode jumboOpcode = opcode.getJumboOpcode(); - if (jumboOpcode == null) { - return null; - } - - if (jumboOpcode.format == Format.Format31c) { - return new Instruction31c(jumboOpcode, (short)getRegisterA(), getReferencedItem()); - } - - return new Instruction41c(jumboOpcode, getRegisterA(), getReferencedItem()); - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction21c(dexFile, opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java deleted file mode 100644 index c063b9aa..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java +++ /dev/null @@ -1,94 +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.EncodedLiteralInstruction; -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 Instruction21h extends Instruction implements SingleRegisterInstruction, EncodedLiteralInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private short litB; - - public Instruction21h(Opcode opcode, short regA, short litB) { - super(opcode); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - this.regA = (byte)regA; - this.litB = litB; - } - - private Instruction21h(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = buffer[bufferIndex + 1]; - this.litB = NumberUtils.decodeShort(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeShort(litB); - } - - public Format getFormat() { - return Format.Format21h; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - public long getLiteral() { - return litB; - } - - public long getDecodedLiteral() { - if (opcode == Opcode.CONST_HIGH16) { - return litB << 16; - } else { - assert opcode == Opcode.CONST_WIDE_HIGH16; - return ((long)litB) << 48; - } - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction21h(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java deleted file mode 100644 index ca507694..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java +++ /dev/null @@ -1,85 +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 Instruction21s extends Instruction implements SingleRegisterInstruction, LiteralInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private short litB; - - public Instruction21s(Opcode opcode, short regA, short litB) { - super(opcode); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - this.regA = (byte)regA; - this.litB = litB; - } - - private Instruction21s(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = buffer[bufferIndex + 1]; - this.litB = NumberUtils.decodeShort(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeShort(litB); - } - - public Format getFormat() { - return Format.Format21s; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - 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 Instruction21s(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java deleted file mode 100644 index f1cd7d27..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java +++ /dev/null @@ -1,103 +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.Code.SingleRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction21t extends OffsetInstruction implements SingleRegisterInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private short targetAddressOffset; - - public Instruction21t(Opcode opcode, short regA, short offB) { - super(opcode); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - if (offB == 0) { - throw new RuntimeException("The address offset cannot be 0."); - } - - this.regA = (byte)regA; - this.targetAddressOffset = offB; - } - - private Instruction21t(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - assert buffer[bufferIndex] == opcode.value; - - regA = buffer[bufferIndex + 1]; - targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex + 2); - assert targetAddressOffset != 0; - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeShort(targetAddressOffset); - } - - public void updateTargetAddressOffset(int targetAddressOffset) { - if (targetAddressOffset < Short.MIN_VALUE || targetAddressOffset > Short.MAX_VALUE) { - throw new RuntimeException("The address offset " + targetAddressOffset + - " is out of range. It must be in [-32768, 32767]"); - } - if (targetAddressOffset == 0) { - throw new RuntimeException("The address offset cannot be 0"); - } - this.targetAddressOffset = (short) targetAddressOffset; - } - - public Format getFormat() { - return Format.Format21t; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - public int getTargetAddressOffset() { - return targetAddressOffset; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction21t(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java deleted file mode 100644 index 19fdbd55..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java +++ /dev/null @@ -1,93 +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.TwoRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; - -public class Instruction22b extends Instruction implements TwoRegisterInstruction, LiteralInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private byte regB; - private byte litC; - - public Instruction22b(Opcode opcode, short regA, short regB, byte litC) { - super(opcode); - - if (regA >= 1 << 8 || - regB >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - this.regA = (byte)regA; - this.regB = (byte)regB; - this.litC = litC; - } - - private Instruction22b(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = buffer[bufferIndex + 1]; - this.regB = buffer[bufferIndex + 2]; - this.litC = buffer[bufferIndex + 3]; - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeByte(regB); - out.writeByte(litC); - } - - public Format getFormat() { - return Format.Format22b; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - public int getRegisterB() { - return regB & 0xFF; - } - - public long getLiteral() { - return litC; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction22b(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java deleted file mode 100644 index 5833a661..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java +++ /dev/null @@ -1,106 +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.InstructionWithReference; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.TwoRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Item; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction22c extends InstructionWithReference implements TwoRegisterInstruction, - InstructionWithJumboVariant { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private byte regB; - - public Instruction22c(Opcode opcode, byte regA, byte regB, Item referencedItem) { - super(opcode, referencedItem); - - 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 Instruction22c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - super(dexFile, opcode, buffer, bufferIndex); - - this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); - this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - if(getReferencedItem().getIndex() > 0xFFFF) { - if (opcode.hasJumboOpcode()) { - throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", - opcode.referenceType.name(), opcode.getJumboOpcode().name)); - } else { - throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); - } - } - - out.writeByte(opcode.value); - out.writeByte((regB << 4) | regA); - out.writeShort(getReferencedItem().getIndex()); - } - - public Format getFormat() { - return Format.Format22c; - } - - public int getRegisterA() { - return regA; - } - - public int getRegisterB() { - return regB; - } - - public Instruction makeJumbo() { - Opcode jumboOpcode = opcode.getJumboOpcode(); - if (jumboOpcode == null) { - return null; - } - - return new Instruction52c(jumboOpcode, getRegisterA(), getRegisterB(), getReferencedItem()); - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction22c(dexFile, opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java deleted file mode 100644 index 9dc62fae..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java +++ /dev/null @@ -1,97 +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.OdexedFieldAccess; -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 Instruction22cs extends Instruction implements TwoRegisterInstruction, OdexedFieldAccess { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private byte regB; - private short fieldOffset; - - public Instruction22cs(Opcode opcode, byte regA, byte regB, int fieldOffset) { - super(opcode); - - if (regA >= 1 << 4 || - regB >= 1 << 4) { - throw new RuntimeException("The register number must be less than v16"); - } - - if (fieldOffset >= 1 << 16) { - throw new RuntimeException("The field offset must be less than 65536"); - } - - this.regA = regA; - this.regB = regB; - this.fieldOffset = (short)fieldOffset; - } - - private Instruction22cs(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); - this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); - this.fieldOffset = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte((regB << 4) | regA); - out.writeShort(fieldOffset); - } - - public Format getFormat() { - return Format.Format22cs; - } - - public int getRegisterA() { - return regA; - } - - public int getRegisterB() { - return regB; - } - - public int getFieldOffset() { - return fieldOffset & 0xFFFF; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction22cs(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java deleted file mode 100644 index 434a1b20..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java +++ /dev/null @@ -1,93 +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.TwoRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction22s extends Instruction implements TwoRegisterInstruction, LiteralInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private byte regB; - private short litC; - - public Instruction22s(Opcode opcode, byte regA, byte regB, short litC) { - 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; - this.litC = litC; - } - - private Instruction22s(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); - this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); - this.litC = NumberUtils.decodeShort(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte((regB << 4) | regA); - out.writeShort(litC); - } - - public Format getFormat() { - return Format.Format22s; - } - - public int getRegisterA() { - return regA; - } - - public int getRegisterB() { - return regB; - } - - public long getLiteral() { - return litC; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction22s(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java deleted file mode 100644 index 56b8298a..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java +++ /dev/null @@ -1,112 +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.Code.TwoRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction22t extends OffsetInstruction implements TwoRegisterInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private byte regB; - private short targetAddressOffset; - - public Instruction22t(Opcode opcode, byte regA, byte regB, short offC) { - super(opcode); - - if (regA >= 16 || - regB >= 16) { - throw new RuntimeException("The register number must be less than v16"); - } - - if (offC == 0) { - throw new RuntimeException("The address offset cannot be 0."); - } - - this.regA = regA; - this.regB = regB; - this.targetAddressOffset = offC; - } - - private Instruction22t(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - assert buffer[bufferIndex] == opcode.value; - - regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); - regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); - targetAddressOffset = NumberUtils.decodeShort(buffer, bufferIndex + 2); - - assert targetAddressOffset != 0; - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte((regB << 4) | regA); - out.writeShort(targetAddressOffset); - } - - public void updateTargetAddressOffset(int targetAddressOffset) { - if (targetAddressOffset < -32768 || targetAddressOffset > 32767) { - throw new RuntimeException("The address offset " + targetAddressOffset + - " is out of range. It must be in [-32768, 32767]"); - } - if (targetAddressOffset == 0) { - throw new RuntimeException("The address offset cannot be 0"); - } - this.targetAddressOffset = (short)targetAddressOffset; - } - - public Format getFormat() { - return Format.Format22t; - } - - public int getRegisterA() { - return regA; - } - - public int getRegisterB() { - return regB; - } - - public int getTargetAddressOffset() { - return targetAddressOffset; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction22t(opcode, buffer, bufferIndex); - } - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java deleted file mode 100644 index dc262897..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java +++ /dev/null @@ -1,88 +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 Instruction22x extends Instruction implements TwoRegisterInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private short regB; - - public Instruction22x(Opcode opcode, short regA, int regB) { - super(opcode); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v16"); - } - - if (regB >= 1 << 16) { - throw new RuntimeException("The register number must be less than v65536"); - } - - this.regA = (byte)regA; - this.regB = (short)regB; - } - - private Instruction22x(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = buffer[bufferIndex + 1]; - this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeShort(regB); - } - - public Format getFormat() { - return Format.Format22x; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - public int getRegisterB() { - return regB & 0xFFFF; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction22x(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java deleted file mode 100644 index b9695fcc..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java +++ /dev/null @@ -1,93 +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.ThreeRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; - -public class Instruction23x extends Instruction implements ThreeRegisterInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private byte regB; - private byte regC; - - public Instruction23x(Opcode opcode, short regA, short regB, short regC) { - super(opcode); - - if (regA >= 1 << 8 || - regB >= 1 << 8 || - regC >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - this.regA = (byte)regA; - this.regB = (byte)regB; - this.regC = (byte)regC; - } - - private Instruction23x(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = buffer[bufferIndex + 1]; - this.regB = buffer[bufferIndex + 2]; - this.regC = buffer[bufferIndex + 3]; - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeByte(regB); - out.writeByte(regC); - } - - public Format getFormat() { - return Format.Format23x; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - public int getRegisterB() { - return regB & 0xFF; - } - - public int getRegisterC() { - return regC & 0xFF; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction23x(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java deleted file mode 100644 index fc83b222..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java +++ /dev/null @@ -1,78 +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; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction30t extends OffsetInstruction { - public static final InstructionFactory Factory = new Factory(); - private int targetAddressOffset; - - public Instruction30t(Opcode opcode, int offA) { - super(opcode); - this.targetAddressOffset = offA; - } - - private Instruction30t(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - assert buffer[bufferIndex] == opcode.value; - - this.targetAddressOffset = NumberUtils.decodeInt(buffer, bufferIndex+2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(0x00); - out.writeInt(targetAddressOffset); - } - - public void updateTargetAddressOffset(int targetAddressOffset) { - this.targetAddressOffset = targetAddressOffset; - } - - public Format getFormat() { - return Format.Format30t; - } - - public int getTargetAddressOffset() { - return targetAddressOffset; - } - - private static class Factory implements InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction30t(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java deleted file mode 100644 index 4c62c00a..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java +++ /dev/null @@ -1,79 +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.InstructionWithReference; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.SingleRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Item; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction31c extends InstructionWithJumboReference implements SingleRegisterInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - - public Instruction31c(Opcode opcode, short regA, Item referencedItem) { - super(opcode, referencedItem); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - this.regA = (byte)regA; - } - - private Instruction31c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - super(dexFile, opcode, buffer, bufferIndex); - - this.regA = buffer[bufferIndex + 1]; - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeInt(getReferencedItem().getIndex()); - } - - public Format getFormat() { - return Format.Format31c; - } - - 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 Instruction31c(dexFile, opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java deleted file mode 100644 index 5c08ce48..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java +++ /dev/null @@ -1,85 +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 Instruction31i extends Instruction implements SingleRegisterInstruction, LiteralInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private int litB; - - public Instruction31i(Opcode opcode, short regA, int litB) { - super(opcode); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - this.regA = (byte)regA; - this.litB = litB; - } - - private Instruction31i(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); - this.litB = NumberUtils.decodeInt(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeInt(litB); - } - - public Format getFormat() { - return Format.Format31i; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - 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 Instruction31i(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java deleted file mode 100644 index 55bcc6bf..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java +++ /dev/null @@ -1,90 +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.Code.SingleRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction31t extends OffsetInstruction implements SingleRegisterInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private int targetAddressOffset; - - public Instruction31t(Opcode opcode, short regA, int offB) { - super(opcode); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - this.regA = (byte)regA; - this.targetAddressOffset = offB; - } - - private Instruction31t(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = buffer[bufferIndex + 1]; - this.targetAddressOffset = NumberUtils.decodeInt(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - //align the address offset so that the absolute address is aligned on a 4-byte boundary (2 code block boundary) - out.writeInt(targetAddressOffset + ((currentCodeAddress + targetAddressOffset) % 2)); - } - - public void updateTargetAddressOffset(int targetAddressOffset) { - this.targetAddressOffset = targetAddressOffset; - } - - public Format getFormat() { - return Format.Format31t; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - public int getTargetAddressOffset() { - return targetAddressOffset; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction31t(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java deleted file mode 100644 index 841ab665..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java +++ /dev/null @@ -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; -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 Instruction32x extends Instruction implements TwoRegisterInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private short regA; - private short regB; - - public Instruction32x(Opcode opcode, int regA, int regB) { - super(opcode); - - if (regA >= 1<<16 || - regB >= 1<<16) { - throw new RuntimeException("The register number must be less than v65536"); - } - - this.regA = (short)regA; - this.regB = (short)regB; - } - - private Instruction32x(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(0); - out.writeShort(regA); - out.writeShort(regB); - } - - public Format getFormat() { - return Format.Format32x; - } - - public int getRegisterA() { - return regA & 0xFFFF; - } - - public int getRegisterB() { - return regB & 0xFFFF; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction32x(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java deleted file mode 100644 index dfe69fec..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java +++ /dev/null @@ -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.Code.Format; - -import org.jf.dexlib.Code.FiveRegisterInstruction; -import org.jf.dexlib.Code.Instruction; -import org.jf.dexlib.Code.InstructionWithReference; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Item; -import org.jf.dexlib.MethodIdItem; -import org.jf.dexlib.TypeIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -import static org.jf.dexlib.Code.Opcode.*; - -public class Instruction35c extends InstructionWithReference implements FiveRegisterInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regCount; - private byte regA; - private byte regD; - private byte regE; - private byte regF; - private byte regG; - - public Instruction35c(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG, - byte regA, Item referencedItem) { - super(opcode, referencedItem); - - if (regCount > 5) { - throw new RuntimeException("regCount cannot be greater than 5"); - } - - if (regD >= 1 << 4 || - regE >= 1 << 4 || - regF >= 1 << 4 || - regG >= 1 << 4 || - regA >= 1 << 4) { - throw new RuntimeException("All register args must fit in 4 bits"); - } - - checkItem(opcode, referencedItem, regCount); - - this.regCount = (byte)regCount; - this.regA = regA; - this.regD = regD; - this.regE = regE; - this.regF = regF; - this.regG = regG; - } - - protected Instruction35c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - super(dexFile, opcode, buffer, bufferIndex); - - this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); - this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); - this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]); - this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]); - this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]); - this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]); - - if (getRegCount() > 5) { - throw new RuntimeException("regCount cannot be greater than 5"); - } - - checkItem(opcode, getReferencedItem(), getRegCount()); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - if(getReferencedItem().getIndex() > 0xFFFF) { - if (opcode.hasJumboOpcode()) { - throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", - opcode.referenceType.name(), opcode.getJumboOpcode().name)); - } else { - throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); - } - } - - out.writeByte(opcode.value); - out.writeByte((regCount << 4) | regA); - out.writeShort(getReferencedItem().getIndex()); - out.writeByte((regE << 4) | regD); - out.writeByte((regG << 4) | regF); - } - - public Format getFormat() { - return Format.Format35c; - } - - public int getRegCount() { - return regCount; - } - - public byte getRegisterA() { - return regA; - } - - public byte getRegisterD() { - return regD; - } - - public byte getRegisterE() { - return regE; - } - - public byte getRegisterF() { - return regF; - } - - public byte getRegisterG() { - return regG; - } - - private static void checkItem(Opcode opcode, Item item, int regCount) { - if (opcode == FILLED_NEW_ARRAY) { - //check data for filled-new-array opcode - String type = ((TypeIdItem) item).getTypeDescriptor(); - if (type.charAt(0) != '[') { - throw new RuntimeException("The type must be an array type"); - } - if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { - throw new RuntimeException("The type cannot be an array of longs or doubles"); - } - } else if (opcode.value >= INVOKE_VIRTUAL.value && opcode.value <= INVOKE_INTERFACE.value || - opcode == INVOKE_DIRECT_EMPTY) { - //check data for invoke-* opcodes - MethodIdItem methodIdItem = (MethodIdItem) item; - int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); - if (opcode != INVOKE_STATIC) { - parameterRegisterCount++; - } - if (parameterRegisterCount != regCount) { - throw new RuntimeException("regCount does not match the number of arguments of the method"); - } - } - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction35c(dexFile, opcode, buffer, bufferIndex); - } - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java deleted file mode 100644 index a204cd71..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35mi.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2011, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib.Code.Format; - -import org.jf.dexlib.Code.*; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - - -public class Instruction35mi extends Instruction implements FiveRegisterInstruction, OdexedInvokeInline { - public static final InstructionFactory Factory = new Factory(); - private byte regCount; - private byte regA; - private byte regD; - private byte regE; - private byte regF; - private byte regG; - private short inlineIndex; - - public Instruction35mi(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG, - byte regA, int inlineIndex) { - super(opcode); - if (regCount > 5) { - throw new RuntimeException("regCount cannot be greater than 5"); - } - - if (regD >= 1 << 4 || - regE >= 1 << 4 || - regF >= 1 << 4 || - regG >= 1 << 4 || - regA >= 1 << 4) { - throw new RuntimeException("All register args must fit in 4 bits"); - } - - if (inlineIndex >= 1 << 16) { - throw new RuntimeException("The method index must be less than 65536"); - } - - this.regCount = (byte)regCount; - this.regA = regA; - this.regD = regD; - this.regE = regE; - this.regF = regF; - this.regG = regG; - this.inlineIndex = (short)inlineIndex; - } - - private Instruction35mi(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); - this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); - this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]); - this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]); - this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]); - this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]); - this.inlineIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte((regCount << 4) | regA); - out.writeShort(inlineIndex); - out.writeByte((regE << 4) | regD); - out.writeByte((regG << 4) | regF); - } - - public Format getFormat() { - return Format.Format35mi; - } - - public int getRegCount() { - return regCount; - } - - public byte getRegisterA() { - return regA; - } - - public byte getRegisterD() { - return regD; - } - - public byte getRegisterE() { - return regE; - } - - public byte getRegisterF() { - return regF; - } - - public byte getRegisterG() { - return regG; - } - - public int getInlineIndex() { - return inlineIndex & 0xFFFF; - } - - private static class Factory implements InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction35mi(opcode, buffer, bufferIndex); - } - } -} - diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java deleted file mode 100644 index 8e9098c1..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java +++ /dev/null @@ -1,136 +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.FiveRegisterInstruction; -import org.jf.dexlib.Code.Instruction; -import org.jf.dexlib.Code.OdexedInvokeVirtual; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - - -public class Instruction35ms extends Instruction implements FiveRegisterInstruction, OdexedInvokeVirtual { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regCount; - private byte regA; - private byte regD; - private byte regE; - private byte regF; - private byte regG; - private short vtableIndex; - - public Instruction35ms(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG, - byte regA, int vtableIndex) { - super(opcode); - if (regCount > 5) { - throw new RuntimeException("regCount cannot be greater than 5"); - } - - if (regD >= 1 << 4 || - regE >= 1 << 4 || - regF >= 1 << 4 || - regG >= 1 << 4 || - regA >= 1 << 4) { - throw new RuntimeException("All register args must fit in 4 bits"); - } - - if (vtableIndex >= 1 << 16) { - throw new RuntimeException("The method index must be less than 65536"); - } - - this.regCount = (byte)regCount; - this.regA = regA; - this.regD = regD; - this.regE = regE; - this.regF = regF; - this.regG = regG; - this.vtableIndex = (short)vtableIndex; - } - - private Instruction35ms(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]); - this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]); - this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]); - this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]); - this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]); - this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]); - this.vtableIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte((regCount << 4) | regA); - out.writeShort(vtableIndex); - out.writeByte((regE << 4) | regD); - out.writeByte((regG << 4) | regF); - } - - public Format getFormat() { - return Format.Format35ms; - } - - public int getRegCount() { - return regCount; - } - - public byte getRegisterA() { - return regA; - } - - public byte getRegisterD() { - return regD; - } - - public byte getRegisterE() { - return regE; - } - - public byte getRegisterF() { - return regF; - } - - public byte getRegisterG() { - return regG; - } - - public int getVtableIndex() { - return vtableIndex & 0xFFFF; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction35ms(opcode, buffer, bufferIndex); - } - } -} - diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java deleted file mode 100644 index c2abc621..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java +++ /dev/null @@ -1,148 +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.InstructionWithReference; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.RegisterRangeInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Item; -import org.jf.dexlib.MethodIdItem; -import org.jf.dexlib.TypeIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -import static org.jf.dexlib.Code.Opcode.*; - -public class Instruction3rc extends InstructionWithReference implements RegisterRangeInstruction, - InstructionWithJumboVariant { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regCount; - private short startReg; - - public Instruction3rc(Opcode opcode, short regCount, int startReg, Item referencedItem) { - super(opcode, referencedItem); - - if (regCount >= 1 << 8) { - throw new RuntimeException("regCount must be less than 256"); - } - if (regCount < 0) { - throw new RuntimeException("regCount cannot be negative"); - } - - if (startReg >= 1 << 16) { - throw new RuntimeException("The beginning register of the range must be less than 65536"); - } - if (startReg < 0) { - throw new RuntimeException("The beginning register of the range cannot be negative"); - } - - this.regCount = (byte)regCount; - this.startReg = (short)startReg; - - checkItem(opcode, referencedItem, regCount); - } - - private Instruction3rc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - super(dexFile, opcode, buffer, bufferIndex); - - this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); - this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); - - checkItem(opcode, getReferencedItem(), getRegCount()); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - if(getReferencedItem().getIndex() > 0xFFFF) { - if (opcode.hasJumboOpcode()) { - throw new RuntimeException(String.format("%s index is too large. Use the %s instruction instead.", - opcode.referenceType.name(), opcode.getJumboOpcode().name)); - } else { - throw new RuntimeException(String.format("%s index is too large.", opcode.referenceType.name())); - } - } - - out.writeByte(opcode.value); - out.writeByte(regCount); - out.writeShort(this.getReferencedItem().getIndex()); - out.writeShort(startReg); - } - - public Format getFormat() { - return Format.Format3rc; - } - - public int getRegCount() { - return (short)(regCount & 0xFF); - } - - public int getStartRegister() { - return startReg & 0xFFFF; - } - - private static void checkItem(Opcode opcode, Item item, int regCount) { - if (opcode == FILLED_NEW_ARRAY_RANGE) { - //check data for filled-new-array/range opcode - String type = ((TypeIdItem) item).getTypeDescriptor(); - if (type.charAt(0) != '[') { - throw new RuntimeException("The type must be an array type"); - } - if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { - throw new RuntimeException("The type cannot be an array of longs or doubles"); - } - } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value || - opcode == INVOKE_OBJECT_INIT_RANGE) { - //check data for invoke-*/range opcodes - MethodIdItem methodIdItem = (MethodIdItem) item; - int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); - if (opcode != INVOKE_STATIC_RANGE) { - parameterRegisterCount++; - } - if (parameterRegisterCount != regCount) { - throw new RuntimeException("regCount does not match the number of arguments of the method"); - } - } - } - - public Instruction makeJumbo() { - Opcode jumboOpcode = opcode.getJumboOpcode(); - if (jumboOpcode == null) { - return null; - } - - return new Instruction5rc(jumboOpcode, getRegCount(), getStartRegister(), getReferencedItem()); - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction3rc(dexFile, opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java deleted file mode 100644 index 8499f6f0..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmi.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2011, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib.Code.Format; - -import org.jf.dexlib.Code.*; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction3rmi extends Instruction implements RegisterRangeInstruction, OdexedInvokeInline { - public static final InstructionFactory Factory = new Factory(); - private byte regCount; - private short startReg; - private short inlineIndex; - - public Instruction3rmi(Opcode opcode, short regCount, int startReg, int inlineIndex) { - super(opcode); - - if (regCount >= 1 << 8) { - throw new RuntimeException("regCount must be less than 256"); - } - if (regCount < 0) { - throw new RuntimeException("regCount cannot be negative"); - } - - if (startReg >= 1 << 16) { - throw new RuntimeException("The beginning register of the range must be less than 65536"); - } - if (startReg < 0) { - throw new RuntimeException("The beginning register of the range cannot be negative"); - } - - if (inlineIndex >= 1 << 16) { - throw new RuntimeException("The method index must be less than 65536"); - } - - this.regCount = (byte)regCount; - this.startReg = (short)startReg; - this.inlineIndex = (short)inlineIndex; - } - - private Instruction3rmi(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); - this.inlineIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regCount); - out.writeShort(inlineIndex); - out.writeShort(startReg); - } - - public Format getFormat() { - return Format.Format3rmi; - } - - public int getRegCount() { - return (short)(regCount & 0xFF); - } - - public int getStartRegister() { - return startReg & 0xFFFF; - } - - public int getInlineIndex() { - return inlineIndex & 0xFFFF; - } - - private static class Factory implements InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction3rmi(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java deleted file mode 100644 index a93c9f72..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java +++ /dev/null @@ -1,107 +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.OdexedInvokeVirtual; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.RegisterRangeInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction3rms extends Instruction implements RegisterRangeInstruction, OdexedInvokeVirtual { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regCount; - private short startReg; - private short vtableIndex; - - public Instruction3rms(Opcode opcode, short regCount, int startReg, int vtableIndex) { - super(opcode); - - if (regCount >= 1 << 8) { - throw new RuntimeException("regCount must be less than 256"); - } - if (regCount < 0) { - throw new RuntimeException("regCount cannot be negative"); - } - - if (startReg >= 1 << 16) { - throw new RuntimeException("The beginning register of the range must be less than 65536"); - } - if (startReg < 0) { - throw new RuntimeException("The beginning register of the range cannot be negative"); - } - - if (vtableIndex >= 1 << 16) { - throw new RuntimeException("The method index must be less than 65536"); - } - - this.regCount = (byte)regCount; - this.startReg = (short)startReg; - this.vtableIndex = (short)vtableIndex; - } - - private Instruction3rms(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); - this.vtableIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regCount); - out.writeShort(vtableIndex); - out.writeShort(startReg); - } - - public Format getFormat() { - return Format.Format3rms; - } - - public int getRegCount() { - return (short)(regCount & 0xFF); - } - - public int getStartRegister() { - return startReg & 0xFFFF; - } - - public int getVtableIndex() { - return vtableIndex & 0xFFFF; - } - - private static class Factory implements Instruction.InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction3rms(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction41c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction41c.java deleted file mode 100644 index ac83cbb6..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction41c.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2011, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib.Code.Format; - -import org.jf.dexlib.Code.Instruction; -import org.jf.dexlib.Code.InstructionWithReference; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.SingleRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Item; -import org.jf.dexlib.TypeIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction41c extends InstructionWithJumboReference implements SingleRegisterInstruction { - public static final InstructionFactory Factory = new Factory(); - private short regA; - - public Instruction41c(Opcode opcode, int regA, Item referencedItem) { - super(opcode, referencedItem); - - if (regA >= 1 << 16) { - throw new RuntimeException("The register number must be less than v65536"); - } - - if (opcode == Opcode.NEW_INSTANCE_JUMBO) { - assert referencedItem instanceof TypeIdItem; - if (((TypeIdItem)referencedItem).getTypeDescriptor().charAt(0) != 'L') { - throw new RuntimeException("Only class references can be used with the new-instance/jumbo opcode"); - } - } - - this.regA = (short)regA; - } - - private Instruction41c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - super(dexFile, opcode, buffer, bufferIndex); - - if (opcode == Opcode.NEW_INSTANCE_JUMBO && - ((TypeIdItem)this.getReferencedItem()).getTypeDescriptor().charAt(0) != 'L') { - - throw new RuntimeException("Only class references can be used with the new-instance/jumbo opcode"); - } - - this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(0xFF); - out.writeByte(opcode.value); - out.writeInt(getReferencedItem().getIndex()); - out.writeShort(getRegisterA()); - } - - public Format getFormat() { - return Format.Format41c; - } - - public int getRegisterA() { - return regA & 0xFFFF; - } - - private static class Factory implements InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction41c(dexFile, opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java deleted file mode 100644 index a627c7a8..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java +++ /dev/null @@ -1,85 +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 Instruction51l extends Instruction implements SingleRegisterInstruction, LiteralInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private byte regA; - private long litB; - - public Instruction51l(Opcode opcode, short regA, long litB) { - super(opcode); - - if (regA >= 1 << 8) { - throw new RuntimeException("The register number must be less than v256"); - } - - this.regA = (byte)regA; - this.litB = litB; - } - - private Instruction51l(Opcode opcode, byte[] buffer, int bufferIndex) { - super(opcode); - - regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]); - litB = NumberUtils.decodeLong(buffer, bufferIndex + 2); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(opcode.value); - out.writeByte(regA); - out.writeLong(litB); - } - - public Format getFormat() { - return Format.Format51l; - } - - public int getRegisterA() { - return regA & 0xFF; - } - - 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 Instruction51l(opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction52c.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction52c.java deleted file mode 100644 index 0d7f3f55..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction52c.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2011, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib.Code.Format; - -import org.jf.dexlib.Code.Instruction; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.TwoRegisterInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Item; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -public class Instruction52c extends InstructionWithJumboReference implements TwoRegisterInstruction { - public static final InstructionFactory Factory = new Factory(); - private short regA; - private short regB; - - public Instruction52c(Opcode opcode, int regA, int regB, Item referencedItem) { - super(opcode, referencedItem); - - if (regA >= 1 << 16) { - throw new RuntimeException("The register number must be less than v65536"); - } - - if (regB >= 1 << 16) { - throw new RuntimeException("The register number must be less than v65536"); - } - - this.regA = (short)regA; - this.regB = (short)regB; - } - - private Instruction52c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - super(dexFile, opcode, buffer, bufferIndex); - - this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6); - this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 8); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(0xFF); - out.writeByte(opcode.value); - out.writeInt(getReferencedItem().getIndex()); - out.writeShort(getRegisterA()); - out.writeShort(getRegisterB()); - } - - public Format getFormat() { - return Format.Format52c; - } - - public int getRegisterA() { - return regA & 0xFFFF; - } - - public int getRegisterB() { - return regB & 0xFFFF; - } - - private static class Factory implements InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction52c(dexFile, opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction5rc.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction5rc.java deleted file mode 100644 index 7c79e252..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction5rc.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2011, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.jf.dexlib.Code.Format; - -import org.jf.dexlib.Code.Instruction; -import org.jf.dexlib.Code.InstructionWithReference; -import org.jf.dexlib.Code.Opcode; -import org.jf.dexlib.Code.RegisterRangeInstruction; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Item; -import org.jf.dexlib.MethodIdItem; -import org.jf.dexlib.TypeIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.NumberUtils; - -import static org.jf.dexlib.Code.Opcode.*; - -public class Instruction5rc extends InstructionWithJumboReference implements RegisterRangeInstruction { - public static final InstructionFactory Factory = new Factory(); - private short regCount; - private short startReg; - - public Instruction5rc(Opcode opcode, int regCount, int startReg, Item referencedItem) { - super(opcode, referencedItem); - - if (regCount >= 1 << 16) { - throw new RuntimeException("regCount must be less than 65536"); - } - if (regCount < 0) { - throw new RuntimeException("regCount cannot be negative"); - } - - if (startReg >= 1 << 16) { - throw new RuntimeException("The beginning register of the range must be less than 65536"); - } - if (startReg < 0) { - throw new RuntimeException("The beginning register of the range cannot be negative"); - } - - this.regCount = (short)regCount; - this.startReg = (short)startReg; - - checkItem(opcode, referencedItem, regCount); - } - - private Instruction5rc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - super(dexFile, opcode, buffer, bufferIndex); - - this.regCount = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 6); - this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 8); - - checkItem(opcode, getReferencedItem(), getRegCount()); - } - - protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.writeByte(0xff); - out.writeByte(opcode.value); - out.writeInt(this.getReferencedItem().getIndex()); - out.writeShort(regCount); - out.writeShort(startReg); - } - - public Format getFormat() { - return Format.Format5rc; - } - - public int getRegCount() { - return regCount & 0xFFFF; - } - - public int getStartRegister() { - return startReg & 0xFFFF; - } - - private static void checkItem(Opcode opcode, Item item, int regCount) { - if (opcode == FILLED_NEW_ARRAY_JUMBO) { - //check data for filled-new-array/jumbo opcode - String type = ((TypeIdItem) item).getTypeDescriptor(); - if (type.charAt(0) != '[') { - throw new RuntimeException("The type must be an array type"); - } - if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { - throw new RuntimeException("The type cannot be an array of longs or doubles"); - } - } else if (opcode.value >= INVOKE_VIRTUAL_JUMBO.value && opcode.value <= INVOKE_INTERFACE_JUMBO.value || - opcode == INVOKE_OBJECT_INIT_JUMBO) { - //check data for invoke-*/range opcodes - MethodIdItem methodIdItem = (MethodIdItem) item; - int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); - if (opcode != INVOKE_STATIC_JUMBO) { - parameterRegisterCount++; - } - if (parameterRegisterCount != regCount) { - throw new RuntimeException("regCount does not match the number of arguments of the method"); - } - } - } - - private static class Factory implements InstructionFactory { - public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) { - return new Instruction5rc(dexFile, opcode, buffer, bufferIndex); - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java deleted file mode 100644 index 6e88861a..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java +++ /dev/null @@ -1,158 +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.MultiOffsetInstruction; -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 PackedSwitchDataPseudoInstruction extends Instruction implements MultiOffsetInstruction { - public static final Instruction.InstructionFactory Factory = new Factory(); - private int firstKey; - private int[] targets; - - @Override - public int getSize(int codeAddress) { - return getTargetCount() * 2 + 4 + (codeAddress % 2); - } - - public PackedSwitchDataPseudoInstruction(int firstKey, int[] targets) { - super(Opcode.NOP); - - if (targets.length > 0xFFFF) { - throw new RuntimeException("The packed-switch data contains too many elements. " + - "The maximum number of switch elements is 65535"); - } - - this.firstKey = firstKey; - this.targets = targets; - } - - public PackedSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) { - super(Opcode.NOP); - - byte opcodeByte = buffer[bufferIndex]; - if (opcodeByte != 0x00) { - throw new RuntimeException("Invalid opcode byte for a PackedSwitchData pseudo-instruction"); - } - byte subopcodeByte = buffer[bufferIndex+1]; - if (subopcodeByte != 0x01) { - throw new RuntimeException("Invalid sub-opcode byte for a PackedSwitchData pseudo-instruction"); - } - - int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - this.firstKey = NumberUtils.decodeInt(buffer, bufferIndex + 4); - this.targets = new int[targetCount]; - - for (int i = 0; i iterateKeysAndTargets() { - return new Iterator() { - final int targetCount = getTargetCount(); - int i = 0; - int value = getFirstKey(); - - PackedSwitchTarget packedSwitchTarget = new PackedSwitchTarget(); - - public boolean hasNext() { - return i 0xFFFF) { - throw new RuntimeException("The sparse-switch data contains too many elements. " + - "The maximum number of switch elements is 65535"); - } - - this.keys = keys; - this.targets = targets; - } - - public SparseSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) { - super(Opcode.NOP); - - byte opcodeByte = buffer[bufferIndex]; - if (opcodeByte != 0x00) { - throw new RuntimeException("Invalid opcode byte for a SparseSwitchData pseudo-instruction"); - } - byte subopcodeByte = buffer[bufferIndex+1]; - if (subopcodeByte != 0x02) { - throw new RuntimeException("Invalid sub-opcode byte for a SparseSwitchData pseudo-instruction"); - } - - int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2); - keys = new int[targetCount]; - targets = new int[targetCount]; - - for (int i=0; i 0) { - int key = keys[0]; - - out.writeInt(key); - - for (int i = 1; i < keys.length; i++) { - key = keys[i]; - assert key >= keys[i - 1]; - out.writeInt(key); - } - - for (int target : targets) { - out.writeInt(target); - } - } - } - - protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) { - out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " + - "sparse-switch-data instruction"); - } - - public void updateTarget(int targetIndex, int targetAddressOffset) { - targets[targetIndex] = targetAddressOffset; - } - - public Format getFormat() { - return Format.SparseSwitchData; - } - - public int getTargetCount() { - return targets.length; - } - - public int[] getTargets() { - return targets; - } - - public int[] getKeys() { - return keys; - } - - public static class SparseSwitchTarget { - public int key; - public int targetAddressOffset; - } - - public Iterator iterateKeysAndTargets() { - return new Iterator() { - final int targetCount = getTargetCount(); - int i = 0; - - SparseSwitchTarget sparseSwitchTarget = new SparseSwitchTarget(); - - public boolean hasNext() { - return i opcodesByName; - - //if the instruction can throw an exception - public static final int CAN_THROW = 0x1; - //if the instruction is an odex only instruction - public static final int ODEX_ONLY = 0x2; - //if execution can continue to the next instruction - public static final int CAN_CONTINUE = 0x4; - //if the instruction sets the "hidden" result register - public static final int SETS_RESULT = 0x8; - //if the instruction sets the value of it's first register - public static final int SETS_REGISTER = 0x10; - //if the instruction sets the value of it's first register to a wide type - public static final int SETS_WIDE_REGISTER = 0x20; - //if the instruction is an odexed iget-quick/iput-quick instruction - public static final int ODEXED_INSTANCE_QUICK = 0x40; - //if the instruction is an odexed iget-volatile/iput-volatile instruction - public static final int ODEXED_INSTANCE_VOLATILE = 0x80; - //if the instruction is an odexed sget-volatile/sput-volatile instruction - public static final int ODEXED_STATIC_VOLATILE = 0x100; - //if the instruction is a jumbo instruction - public static final int JUMBO_OPCODE = 0x200; - //if the instruction can initialize an uninitialized object reference - public static final int CAN_INITIALIZE_REFERENCE = 0x400; - - static { - opcodesByValue = new Opcode[256]; - expandedOpcodesByValue = new Opcode[256]; - opcodesByName = new HashMap(); - - for (Opcode opcode: Opcode.values()) { - //INVOKE_DIRECT_EMPTY was changed to INVOKE_OBJECT_INIT_RANGE in ICS - if (opcode != INVOKE_DIRECT_EMPTY) { - if (((opcode.value >> 8) & 0xFF) == 0x00) { - opcodesByValue[opcode.value & 0xFF] = opcode; - } else { - assert ((opcode.value >> 8) & 0xFF) == 0xFF; - expandedOpcodesByValue[opcode.value & 0xFF] = opcode; - } - opcodesByName.put(opcode.name.hashCode(), opcode); - } - } - } - - public static Opcode getOpcodeByName(String opcodeName) { - return opcodesByName.get(opcodeName.toLowerCase().hashCode()); - } - - public static Opcode getOpcodeByValue(short opcodeValue) { - if (((opcodeValue >> 8) & 0xFF) == 0x00) { - return opcodesByValue[opcodeValue & 0xFF]; - } else { - assert ((opcodeValue >> 8) & 0xFF) == 0xFF; - return expandedOpcodesByValue[opcodeValue & 0xFF]; - } - } - - private static void removeOpcodes(Opcode... toRemove) { - for (Opcode opcode: toRemove) { - opcodesByName.remove(opcode.name.toLowerCase().hashCode()); - - if (((opcode.value >> 8) & 0xFF) == 0x00) { - opcodesByValue[opcode.value] = null; - } else { - expandedOpcodesByValue[opcode.value & 0xFF] = null; - } - } - } - - private static void addOpcodes(Opcode... toAdd) { - for (Opcode opcode: toAdd) { - if (((opcode.value >> 8) & 0xFF) == 0x00) { - opcodesByValue[opcode.value & 0xFF] = opcode; - } else { - assert ((opcode.value >> 8) & 0xFF) == 0xFF; - expandedOpcodesByValue[opcode.value & 0xFF] = opcode; - } - opcodesByName.put(opcode.name.hashCode(), opcode); - } - } - - /** - * This will add/remove/replace various opcodes in the value/name maps as needed, - * based on the idiosyncrasies of that api level - * @param apiLevel - */ - public static void updateMapsForApiLevel(int apiLevel, boolean includeJumbo) { - if (apiLevel < 5) { - removeOpcodes(THROW_VERIFICATION_ERROR); - } - if (apiLevel < 8) { - removeOpcodes(EXECUTE_INLINE_RANGE); - } - if (apiLevel < 9) { - removeOpcodes(IGET_VOLATILE, IPUT_VOLATILE, SGET_VOLATILE, SPUT_VOLATILE, IGET_OBJECT_VOLATILE, - IGET_WIDE_VOLATILE, IPUT_WIDE_VOLATILE, SGET_WIDE_VOLATILE, SPUT_WIDE_VOLATILE, - IPUT_OBJECT_VOLATILE, SGET_OBJECT_VOLATILE, SPUT_OBJECT_VOLATILE); - } - if (apiLevel < 11) { - removeOpcodes(RETURN_VOID_BARRIER); - } - if (apiLevel < 14) { - removeOpcodes(INVOKE_OBJECT_INIT_RANGE); - addOpcodes(INVOKE_DIRECT_EMPTY); - } - if (apiLevel < 14 || !includeJumbo) { - removeOpcodes(CONST_CLASS_JUMBO, CHECK_CAST_JUMBO, INSTANCE_OF_JUMBO, NEW_INSTANCE_JUMBO, - NEW_ARRAY_JUMBO, FILLED_NEW_ARRAY_JUMBO, IGET_JUMBO, IGET_WIDE_JUMBO, IGET_OBJECT_JUMBO, - IGET_BOOLEAN_JUMBO, IGET_BYTE_JUMBO, IGET_CHAR_JUMBO, IGET_SHORT_JUMBO, IPUT_JUMBO, IPUT_WIDE_JUMBO, - IPUT_OBJECT_JUMBO, IPUT_BOOLEAN_JUMBO, IPUT_BYTE_JUMBO, IPUT_CHAR_JUMBO, IPUT_SHORT_JUMBO, - SGET_JUMBO, SGET_WIDE_JUMBO, SGET_OBJECT_JUMBO, SGET_BOOLEAN_JUMBO, SGET_BYTE_JUMBO, - SGET_CHAR_JUMBO, SGET_SHORT_JUMBO, SPUT_JUMBO, SPUT_WIDE_JUMBO, SPUT_OBJECT_JUMBO, - SPUT_BOOLEAN_JUMBO, SPUT_BYTE_JUMBO, SPUT_CHAR_JUMBO, SPUT_SHORT_JUMBO, INVOKE_VIRTUAL_JUMBO, - INVOKE_SUPER_JUMBO, INVOKE_DIRECT_JUMBO, INVOKE_STATIC_JUMBO, INVOKE_INTERFACE_JUMBO, - INVOKE_OBJECT_INIT_JUMBO, IGET_VOLATILE_JUMBO, IGET_WIDE_VOLATILE_JUMBO, - IGET_OBJECT_VOLATILE_JUMBO, IPUT_VOLATILE_JUMBO, IPUT_WIDE_VOLATILE_JUMBO, - IPUT_OBJECT_VOLATILE_JUMBO, SGET_VOLATILE_JUMBO, SGET_WIDE_VOLATILE_JUMBO, - SGET_OBJECT_VOLATILE_JUMBO, SPUT_VOLATILE_JUMBO, SPUT_WIDE_VOLATILE_JUMBO, - SPUT_OBJECT_VOLATILE_JUMBO); - } - } - - public final short value; - public final String name; - public final ReferenceType referenceType; - public final Format format; - public final int flags; - private final short jumboOpcode; - - Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format) { - this(opcodeValue, opcodeName, referenceType, format, 0); - } - - Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format, int flags) { - this(opcodeValue, opcodeName, referenceType, format, flags, (short)-1); - } - - Opcode(short opcodeValue, String opcodeName, ReferenceType referenceType, Format format, int flags, short jumboOpcodeValue) { - this.value = opcodeValue; - this.name = opcodeName; - this.referenceType = referenceType; - this.format = format; - this.flags = flags; - this.jumboOpcode = jumboOpcodeValue; - } - - public final boolean canThrow() { - return (flags & CAN_THROW) != 0; - } - - public final boolean odexOnly() { - return (flags & ODEX_ONLY) != 0; - } - - public final boolean canContinue() { - return (flags & CAN_CONTINUE) != 0; - } - - public final boolean setsResult() { - return (flags & SETS_RESULT) != 0; - } - - public final boolean setsRegister() { - return (flags & SETS_REGISTER) != 0; - } - - public final boolean setsWideRegister() { - return (flags & SETS_WIDE_REGISTER) != 0; - } - - public final boolean isOdexedInstanceQuick() { - return (flags & ODEXED_INSTANCE_QUICK) != 0; - } - - public final boolean isOdexedInstanceVolatile() { - return (flags & ODEXED_INSTANCE_VOLATILE) != 0; - } - - public final boolean isOdexedStaticVolatile() { - return (flags & ODEXED_STATIC_VOLATILE) != 0; - } - - public final boolean isJumboOpcode() { - return (flags & JUMBO_OPCODE) != 0; - } - - public final boolean canInitializeReference() { - return (flags & CAN_INITIALIZE_REFERENCE) != 0; - } - - public final boolean hasJumboOpcode() { - return jumboOpcode != -1 && Opcode.getOpcodeByValue(jumboOpcode) != null; - } - - public final Opcode getJumboOpcode() { - return Opcode.getOpcodeByValue(jumboOpcode); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java deleted file mode 100644 index f6d147ab..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ReferenceType.java +++ /dev/null @@ -1,80 +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; - -import org.jf.dexlib.*; - -public enum ReferenceType -{ - string(-1), - type(0), - field(1), - method(2), - none(-1); - - private int validationErrorReferenceType; - - private ReferenceType(int validationErrorReferenceType) { - this.validationErrorReferenceType = validationErrorReferenceType; - } - - public boolean checkItem(Item item) { - switch (this) { - case string: - return item instanceof StringIdItem; - case type: - return item instanceof TypeIdItem; - case field: - return item instanceof FieldIdItem; - case method: - return item instanceof MethodIdItem; - } - return false; - } - - public static ReferenceType fromValidationErrorReferenceType(int validationErrorReferenceType) { - switch (validationErrorReferenceType) { - case 0: - return type; - case 1: - return field; - case 2: - return method; - } - return null; - } - - public int getValidationErrorReferenceType() { - if (validationErrorReferenceType == -1) { - throw new RuntimeException("This reference type cannot be referenced from a throw-validation-error" + - " instruction"); - } - return validationErrorReferenceType; - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java deleted file mode 100644 index 21e37196..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/RegisterRangeInstruction.java +++ /dev/null @@ -1,33 +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 RegisterRangeInstruction extends InvokeInstruction { - int getStartRegister(); -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java deleted file mode 100644 index 0cc46561..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/SingleRegisterInstruction.java +++ /dev/null @@ -1,33 +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 SingleRegisterInstruction { - int getRegisterA(); -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java deleted file mode 100644 index 83bbb808..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/ThreeRegisterInstruction.java +++ /dev/null @@ -1,33 +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 ThreeRegisterInstruction extends TwoRegisterInstruction { - int getRegisterC(); -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java deleted file mode 100644 index 022a1458..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/TwoRegisterInstruction.java +++ /dev/null @@ -1,35 +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 TwoRegisterInstruction extends SingleRegisterInstruction { - int getRegisterA(); - int getRegisterB(); - -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java deleted file mode 100644 index 622be5d3..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/VerificationErrorType.java +++ /dev/null @@ -1,99 +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; - -import java.util.HashMap; - -public enum VerificationErrorType { - None(0, "no-error"), - Generic(1, "generic-error"), - NoClass(2, "no-such-class"), - NoField(3, "no-such-field"), - NoMethod(4, "no-such-method"), - AccessClass(5, "illegal-class-access"), - AccessField(6, "illegal-field-access"), - AccessMethod(7, "illegal-method-access"), - ClassChange(8, "class-change-error"), - Instantiation(9, "instantiation-error"); - - private static HashMap verificationErrorTypesByName; - - static { - verificationErrorTypesByName = new HashMap(); - - for (VerificationErrorType verificationErrorType: VerificationErrorType.values()) { - verificationErrorTypesByName.put(verificationErrorType.getName(), verificationErrorType); - } - } - - private int value; - private String name; - private VerificationErrorType(int value, String name) { - this.value = value; - this.name = name; - } - - public int getValue() { - return value; - } - - public String getName() { - return name; - } - - public static VerificationErrorType fromString(String validationErrorType) { - return verificationErrorTypesByName.get(validationErrorType); - } - - public static VerificationErrorType getValidationErrorType(int validationErrorType) { - switch (validationErrorType) { - case 0: - return None; - case 1: - return Generic; - case 2: - return NoClass; - case 3: - return NoField; - case 4: - return NoMethod; - case 5: - return AccessClass; - case 6: - return AccessField; - case 7: - return AccessMethod; - case 8: - return ClassChange; - case 9: - return Instantiation; - } - return null; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/CodeItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/CodeItem.java deleted file mode 100644 index e56bfb58..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/CodeItem.java +++ /dev/null @@ -1,1083 +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.Code.Format.*; -import org.jf.dexlib.Code.*; -import org.jf.dexlib.Debug.DebugInstructionIterator; -import org.jf.dexlib.Debug.DebugOpcode; -import org.jf.dexlib.Util.*; - -import java.util.ArrayList; -import java.util.List; - -public class CodeItem extends Item { - private int registerCount; - private int inWords; - private int outWords; - private DebugInfoItem debugInfo; - private Instruction[] instructions; - private TryItem[] tries; - private EncodedCatchHandler[] encodedCatchHandlers; - - private ClassDataItem.EncodedMethod parent; - - /** - * Creates a new uninitialized CodeItem - * @param dexFile The DexFile that this item belongs to - */ - public CodeItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new CodeItem with the given values. - * @param dexFile The DexFile that this item belongs to - * @param registerCount the number of registers that the method containing this code uses - * @param inWords the number of 2-byte words that the parameters to the method containing this code take - * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code - * @param debugInfo the debug information for this code/method - * @param instructions the instructions for this code item - * @param tries an array of the tries defined for this code/method - * @param encodedCatchHandlers an array of the exception handlers defined for this code/method - */ - private CodeItem(DexFile dexFile, - int registerCount, - int inWords, - int outWords, - DebugInfoItem debugInfo, - Instruction[] instructions, - TryItem[] tries, - EncodedCatchHandler[] encodedCatchHandlers) { - super(dexFile); - - this.registerCount = registerCount; - this.inWords = inWords; - this.outWords = outWords; - this.debugInfo = debugInfo; - if (debugInfo != null) { - debugInfo.setParent(this); - } - - this.instructions = instructions; - this.tries = tries; - this.encodedCatchHandlers = encodedCatchHandlers; - } - - /** - * Returns a new CodeItem with the given values. - * @param dexFile The DexFile that this item belongs to - * @param registerCount the number of registers that the method containing this code uses - * @param inWords the number of 2-byte words that the parameters to the method containing this code take - * @param outWords the maximum number of 2-byte words for the arguments of any method call in this code - * @param debugInfo the debug information for this code/method - * @param instructions the instructions for this code item - * @param tries a list of the tries defined for this code/method or null if none - * @param encodedCatchHandlers a list of the exception handlers defined for this code/method or null if none - * @return a new CodeItem with the given values. - */ - public static CodeItem internCodeItem(DexFile dexFile, - int registerCount, - int inWords, - int outWords, - DebugInfoItem debugInfo, - List instructions, - List tries, - List encodedCatchHandlers) { - TryItem[] triesArray = null; - EncodedCatchHandler[] encodedCatchHandlersArray = null; - Instruction[] instructionsArray = null; - - if (tries != null && tries.size() > 0) { - triesArray = new TryItem[tries.size()]; - tries.toArray(triesArray); - } - - if (encodedCatchHandlers != null && encodedCatchHandlers.size() > 0) { - encodedCatchHandlersArray = new EncodedCatchHandler[encodedCatchHandlers.size()]; - encodedCatchHandlers.toArray(encodedCatchHandlersArray); - } - - if (instructions != null && instructions.size() > 0) { - instructionsArray = new Instruction[instructions.size()]; - instructions.toArray(instructionsArray); - } - - CodeItem codeItem = new CodeItem(dexFile, registerCount, inWords, outWords, debugInfo, instructionsArray, - triesArray, encodedCatchHandlersArray); - return dexFile.CodeItemsSection.intern(codeItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - this.registerCount = in.readShort(); - this.inWords = in.readShort(); - this.outWords = in.readShort(); - int triesCount = in.readShort(); - this.debugInfo = (DebugInfoItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_DEBUG_INFO_ITEM, - in.readInt()); - if (this.debugInfo != null) { - this.debugInfo.setParent(this); - } - - int instructionCount = in.readInt(); - - final ArrayList instructionList = new ArrayList(); - - byte[] encodedInstructions = in.readBytes(instructionCount * 2); - InstructionIterator.IterateInstructions(dexFile, encodedInstructions, - new InstructionIterator.ProcessInstructionDelegate() { - public void ProcessInstruction(int codeAddress, Instruction instruction) { - instructionList.add(instruction); - } - }); - - this.instructions = new Instruction[instructionList.size()]; - instructionList.toArray(instructions); - - if (triesCount > 0) { - in.alignTo(4); - - //we need to read in the catch handlers first, so save the offset to the try items for future reference - int triesOffset = in.getCursor(); - in.setCursor(triesOffset + 8 * triesCount); - - //read in the encoded catch handlers - int encodedHandlerStart = in.getCursor(); - int handlerCount = in.readUnsignedLeb128(); - SparseArray handlerMap = new SparseArray(handlerCount); - encodedCatchHandlers = new EncodedCatchHandler[handlerCount]; - for (int i=0; i 0) { - offset = AlignmentUtils.alignOffset(offset, 4); - - offset += tries.length * 8; - int encodedCatchHandlerBaseOffset = offset; - offset += Leb128Utils.unsignedLeb128Size(encodedCatchHandlers.length); - for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { - offset = encodedCatchHandler.place(offset, encodedCatchHandlerBaseOffset); - } - } - return offset; - } - - /** {@inheritDoc} */ - protected void writeItem(final AnnotatedOutput out) { - int instructionsLength = getInstructionsLength(); - - if (out.annotates()) { - out.annotate(0, parent.method.getMethodString()); - out.annotate(2, "registers_size: 0x" + Integer.toHexString(registerCount) + " (" + registerCount + ")"); - out.annotate(2, "ins_size: 0x" + Integer.toHexString(inWords) + " (" + inWords + ")"); - out.annotate(2, "outs_size: 0x" + Integer.toHexString(outWords) + " (" + outWords + ")"); - int triesLength = tries==null?0:tries.length; - out.annotate(2, "tries_size: 0x" + Integer.toHexString(triesLength) + " (" + triesLength + ")"); - if (debugInfo == null) { - out.annotate(4, "debug_info_off:"); - } else { - out.annotate(4, "debug_info_off: 0x" + Integer.toHexString(debugInfo.getOffset())); - } - out.annotate(4, "insns_size: 0x" + Integer.toHexString(instructionsLength) + " (" + - (instructionsLength) + ")"); - } - - out.writeShort(registerCount); - out.writeShort(inWords); - out.writeShort(outWords); - if (tries == null) { - out.writeShort(0); - } else { - out.writeShort(tries.length); - } - if (debugInfo == null) { - out.writeInt(0); - } else { - out.writeInt(debugInfo.getOffset()); - } - - out.writeInt(instructionsLength); - - int currentCodeAddress = 0; - for (Instruction instruction: instructions) { - currentCodeAddress = instruction.write(out, currentCodeAddress); - } - - if (tries != null && tries.length > 0) { - if (out.annotates()) { - if ((currentCodeAddress % 2) != 0) { - out.annotate("padding"); - out.writeShort(0); - } - - int index = 0; - for (TryItem tryItem: tries) { - out.annotate(0, "[0x" + Integer.toHexString(index++) + "] try_item"); - out.indent(); - tryItem.writeTo(out); - out.deindent(); - } - - out.annotate("handler_count: 0x" + Integer.toHexString(encodedCatchHandlers.length) + "(" + - encodedCatchHandlers.length + ")"); - out.writeUnsignedLeb128(encodedCatchHandlers.length); - - index = 0; - for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { - out.annotate(0, "[" + Integer.toHexString(index++) + "] encoded_catch_handler"); - out.indent(); - encodedCatchHandler.writeTo(out); - out.deindent(); - } - } else { - if ((currentCodeAddress % 2) != 0) { - out.writeShort(0); - } - - for (TryItem tryItem: tries) { - tryItem.writeTo(out); - } - - out.writeUnsignedLeb128(encodedCatchHandlers.length); - - for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { - encodedCatchHandler.writeTo(out); - } - } - } - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_CODE_ITEM; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - if (this.parent == null) { - return "code_item @0x" + Integer.toHexString(getOffset()); - } - return "code_item @0x" + Integer.toHexString(getOffset()) + " (" + parent.method.getMethodString() + ")"; - } - - /** {@inheritDoc} */ - public int compareTo(CodeItem other) { - if (parent == null) { - if (other.parent == null) { - return 0; - } - return -1; - } - if (other.parent == null) { - return 1; - } - return parent.method.compareTo(other.parent.method); - } - - /** - * @return the register count - */ - public int getRegisterCount() { - return registerCount; - } - - /** - * @return an array of the instructions in this code item - */ - public Instruction[] getInstructions() { - return instructions; - } - - /** - * @return an array of the TryItem objects in this CodeItem - */ - public TryItem[] getTries() { - return tries; - } - - /** - * @return an array of the EncodedCatchHandler objects in this CodeItem - */ - public EncodedCatchHandler[] getHandlers() { - return encodedCatchHandlers; - } - - /** - * @return the DebugInfoItem associated with this CodeItem - */ - public DebugInfoItem getDebugInfo() { - return debugInfo; - } - - /** - * @return the number of 2-byte words that the parameters to the method containing this code take - */ - public int getInWords() { - return inWords; - } - - /** - * @return the maximum number of 2-byte words for the arguments of any method call in this code - */ - public int getOutWords() { - return outWords; - } - - /** - * Sets the MethodIdItem of the method that this CodeItem is associated with - * @param encodedMethod the EncodedMethod of the method that this CodeItem is associated - * with - */ - protected void setParent(ClassDataItem.EncodedMethod encodedMethod) { - this.parent = encodedMethod; - } - - /** - * @return the MethodIdItem of the method that this CodeItem belongs to - */ - public ClassDataItem.EncodedMethod getParent() { - return parent; - } - - /** - * Used by OdexUtil to update this CodeItem with a deodexed version of the instructions - * @param newInstructions the new instructions to use for this code item - */ - public void updateCode(Instruction[] newInstructions) { - this.instructions = newInstructions; - } - - /** - * @return The length of the instructions in this CodeItem, in 2-byte code blocks - */ - private int getInstructionsLength() { - int currentCodeAddress = 0; - for (Instruction instruction: instructions) { - currentCodeAddress += instruction.getSize(currentCodeAddress); - } - return currentCodeAddress; - } - - /** - * Go through the instructions and perform any of the following fixes that are applicable - * - Replace const-string instruction with const-string/jumbo, when the string index is too big - * - Replace goto and goto/16 with a larger version of goto, when the target is too far away - * TODO: we should be able to replace if-* instructions with targets that are too far away with a negated if followed by a goto/32 to the original target - * TODO: remove multiple nops that occur before a switch/array data pseudo instruction. In some cases, multiple smali-baksmali cycles with changes in between could cause nops to start piling up - * TODO: in case of non-range invoke with a jumbo-sized method reference, we could check if the registers are sequential, and replace it with the jumbo variant (which only takes a register range) - * - * The above fixes are applied iteratively, until no more fixes have been performed - */ - public void fixInstructions(boolean fixJumbo, boolean fixGoto) { - try { - boolean didSomething = false; - - do - { - didSomething = false; - - int currentCodeAddress = 0; - for (int i=0; i 0xFFFF) { - - InstructionWithJumboVariant instructionWithJumboVariant = - (InstructionWithJumboVariant)referenceInstruction; - - Instruction jumboInstruction = instructionWithJumboVariant.makeJumbo(); - if (jumboInstruction != null) { - replaceInstructionAtAddress(currentCodeAddress, - instructionWithJumboVariant.makeJumbo()); - didSomething = true; - break; - } - } - } - - currentCodeAddress += instruction.getSize(currentCodeAddress); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, "Error while attempting to fix " + - instruction.opcode.name + " instruction at address " + currentCodeAddress); - } - } - }while(didSomething); - } catch (Exception ex) { - throw this.addExceptionContext(ex); - } - } - - private void replaceInstructionAtAddress(int codeAddress, Instruction replacementInstruction) { - Instruction originalInstruction = null; - - int[] originalInstructionCodeAddresses = new int[instructions.length+1]; - SparseIntArray originalSwitchAddressByOriginalSwitchDataAddress = new SparseIntArray(); - - int currentCodeAddress = 0; - int instructionIndex = 0; - int i; - for (i=0; i= 0; - int originalAddress = originalAddressByNewAddress.get(currentCodeAddress); - - int originalInstructionTarget = originalAddress + offsetInstruction.getTargetAddressOffset(); - - assert newAddressByOriginalAddress.indexOfKey(originalInstructionTarget) >= 0; - int newInstructionTarget = newAddressByOriginalAddress.get(originalInstructionTarget); - - int newCodeAddress = (newInstructionTarget - currentCodeAddress); - - if (newCodeAddress != offsetInstruction.getTargetAddressOffset()) { - offsetInstruction.updateTargetAddressOffset(newCodeAddress); - } - } else if (instruction instanceof MultiOffsetInstruction) { - MultiOffsetInstruction multiOffsetInstruction = (MultiOffsetInstruction)instruction; - - assert originalAddressByNewAddress.indexOfKey(currentCodeAddress) >= 0; - int originalDataAddress = originalAddressByNewAddress.get(currentCodeAddress); - - int originalSwitchAddress = - originalSwitchAddressByOriginalSwitchDataAddress.get(originalDataAddress, -1); - if (originalSwitchAddress == -1) { - //TODO: maybe we could just remove the unreferenced switch data? - throw new RuntimeException("This method contains an unreferenced switch data block at address " + - + currentCodeAddress + " and can't be automatically fixed."); - } - - assert newAddressByOriginalAddress.indexOfKey(originalSwitchAddress) >= 0; - int newSwitchAddress = newAddressByOriginalAddress.get(originalSwitchAddress); - - int[] targets = multiOffsetInstruction.getTargets(); - for (int t=0; t= 0; - int newTargetCodeAddress = newAddressByOriginalAddress.get(originalTargetCodeAddress); - int newCodeAddress = newTargetCodeAddress - newSwitchAddress; - if (newCodeAddress != targets[t]) { - multiOffsetInstruction.updateTarget(t, newCodeAddress); - } - } - } - currentCodeAddress += instruction.getSize(currentCodeAddress); - } - - if (debugInfo != null) { - final byte[] encodedDebugInfo = debugInfo.getEncodedDebugInfo(); - - ByteArrayInput debugInput = new ByteArrayInput(encodedDebugInfo); - - DebugInstructionFixer debugInstructionFixer = new DebugInstructionFixer(encodedDebugInfo, - newAddressByOriginalAddress); - DebugInstructionIterator.IterateInstructions(debugInput, debugInstructionFixer); - - if (debugInstructionFixer.result != null) { - debugInfo.setEncodedDebugInfo(debugInstructionFixer.result); - } - } - - if (encodedCatchHandlers != null) { - for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) { - if (encodedCatchHandler.catchAllHandlerAddress != -1) { - assert newAddressByOriginalAddress.indexOfKey(encodedCatchHandler.catchAllHandlerAddress) >= 0; - encodedCatchHandler.catchAllHandlerAddress = - newAddressByOriginalAddress.get(encodedCatchHandler.catchAllHandlerAddress); - } - - for (EncodedTypeAddrPair handler: encodedCatchHandler.handlers) { - assert newAddressByOriginalAddress.indexOfKey(handler.handlerAddress) >= 0; - handler.handlerAddress = newAddressByOriginalAddress.get(handler.handlerAddress); - } - } - } - - if (this.tries != null) { - for (TryItem tryItem: tries) { - int startAddress = tryItem.startCodeAddress; - int endAddress = tryItem.startCodeAddress + tryItem.tryLength; - - assert newAddressByOriginalAddress.indexOfKey(startAddress) >= 0; - tryItem.startCodeAddress = newAddressByOriginalAddress.get(startAddress); - - assert newAddressByOriginalAddress.indexOfKey(endAddress) >= 0; - tryItem.tryLength = newAddressByOriginalAddress.get(endAddress) - tryItem.startCodeAddress; - } - } - } - - private class DebugInstructionFixer extends DebugInstructionIterator.ProcessRawDebugInstructionDelegate { - private int currentCodeAddress = 0; - private SparseIntArray newAddressByOriginalAddress; - private final byte[] originalEncodedDebugInfo; - public byte[] result = null; - - public DebugInstructionFixer(byte[] originalEncodedDebugInfo, SparseIntArray newAddressByOriginalAddress) { - this.newAddressByOriginalAddress = newAddressByOriginalAddress; - this.originalEncodedDebugInfo = originalEncodedDebugInfo; - } - - - @Override - public void ProcessAdvancePC(int startDebugOffset, int debugInstructionLength, int codeAddressDelta) { - currentCodeAddress += codeAddressDelta; - - if (result != null) { - return; - } - - int newCodeAddress = newAddressByOriginalAddress.get(currentCodeAddress, -1); - - //The address might not point to an actual instruction in some cases, for example, if an AdvancePC - //instruction was inserted just before a "special" instruction, to fix up the addresses for a previous - //instruction replacement. - //In this case, it should be safe to skip, because there will be another AdvancePC/SpecialOpcode that will - //bump up the address to point to a valid instruction before anything (line/local/etc.) is emitted - if (newCodeAddress == -1) { - return; - } - - if (newCodeAddress != currentCodeAddress) { - int newCodeAddressDelta = newCodeAddress - (currentCodeAddress - codeAddressDelta); - assert newCodeAddressDelta > 0; - int codeAddressDeltaLeb128Size = Leb128Utils.unsignedLeb128Size(newCodeAddressDelta); - - //if the length of the new code address delta is the same, we can use the existing buffer - if (codeAddressDeltaLeb128Size + 1 == debugInstructionLength) { - result = originalEncodedDebugInfo; - Leb128Utils.writeUnsignedLeb128(newCodeAddressDelta, result, startDebugOffset+1); - } else { - //The length of the new code address delta is different, so create a new buffer with enough - //additional space to accomodate the new code address delta value. - result = new byte[originalEncodedDebugInfo.length + codeAddressDeltaLeb128Size - - (debugInstructionLength - 1)]; - - System.arraycopy(originalEncodedDebugInfo, 0, result, 0, startDebugOffset); - - result[startDebugOffset] = DebugOpcode.DBG_ADVANCE_PC.value; - Leb128Utils.writeUnsignedLeb128(newCodeAddressDelta, result, startDebugOffset+1); - - System.arraycopy(originalEncodedDebugInfo, startDebugOffset + debugInstructionLength, result, - startDebugOffset + codeAddressDeltaLeb128Size + 1, - originalEncodedDebugInfo.length - (startDebugOffset + codeAddressDeltaLeb128Size + 1)); - } - } - } - - @Override - public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDelta, - int codeAddressDelta) { - currentCodeAddress += codeAddressDelta; - if (result != null) { - return; - } - - int newCodeAddress = newAddressByOriginalAddress.get(currentCodeAddress, -1); - assert newCodeAddress != -1; - - if (newCodeAddress != currentCodeAddress) { - int newCodeAddressDelta = newCodeAddress - (currentCodeAddress - codeAddressDelta); - assert newCodeAddressDelta > 0; - - //if the new code address delta won't fit in the special opcode, we need to insert - //an additional DBG_ADVANCE_PC opcode - if (lineDelta < 2 && newCodeAddressDelta > 16 || lineDelta > 1 && newCodeAddressDelta > 15) { - int additionalCodeAddressDelta = newCodeAddress - currentCodeAddress; - int additionalCodeAddressDeltaLeb128Size = Leb128Utils.signedLeb128Size(additionalCodeAddressDelta); - - //create a new buffer with enough additional space for the new opcode - result = new byte[originalEncodedDebugInfo.length + additionalCodeAddressDeltaLeb128Size + 1]; - - System.arraycopy(originalEncodedDebugInfo, 0, result, 0, startDebugOffset); - result[startDebugOffset] = 0x01; //DBG_ADVANCE_PC - Leb128Utils.writeUnsignedLeb128(additionalCodeAddressDelta, result, startDebugOffset+1); - System.arraycopy(originalEncodedDebugInfo, startDebugOffset, result, - startDebugOffset+additionalCodeAddressDeltaLeb128Size+1, - result.length - (startDebugOffset+additionalCodeAddressDeltaLeb128Size+1)); - } else { - result = originalEncodedDebugInfo; - result[startDebugOffset] = DebugInfoBuilder.calculateSpecialOpcode(lineDelta, - newCodeAddressDelta); - } - } - } - } - - public static class TryItem { - /** - * The address (in 2-byte words) within the code where the try block starts - */ - private int startCodeAddress; - - /** - * The number of 2-byte words that the try block covers - */ - private int tryLength; - - /** - * The associated exception handler - */ - public final EncodedCatchHandler encodedCatchHandler; - - /** - * Construct a new TryItem with the given values - * @param startCodeAddress the code address within the code where the try block starts - * @param tryLength the number of code blocks that the try block covers - * @param encodedCatchHandler the associated exception handler - */ - public TryItem(int startCodeAddress, int tryLength, EncodedCatchHandler encodedCatchHandler) { - this.startCodeAddress = startCodeAddress; - this.tryLength = tryLength; - this.encodedCatchHandler = encodedCatchHandler; - } - - /** - * This is used internally to construct a new TryItem while reading in a DexFile - * @param in the Input object to read the TryItem from - * @param encodedCatchHandlers a SparseArray of the EncodedCatchHandlers for this CodeItem. The - * key should be the offset of the EncodedCatchHandler from the beginning of the encoded_catch_handler_list - * structure. - */ - private TryItem(Input in, SparseArray encodedCatchHandlers) { - startCodeAddress = in.readInt(); - tryLength = in.readShort(); - - encodedCatchHandler = encodedCatchHandlers.get(in.readShort()); - if (encodedCatchHandler == null) { - throw new RuntimeException("Could not find the EncodedCatchHandler referenced by this TryItem"); - } - } - - /** - * Writes the TryItem to the given AnnotatedOutput object - * @param out the AnnotatedOutput object to write to - */ - private void writeTo(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate(4, "start_addr: 0x" + Integer.toHexString(startCodeAddress)); - out.annotate(2, "try_length: 0x" + Integer.toHexString(tryLength) + " (" + tryLength + - ")"); - out.annotate(2, "handler_off: 0x" + Integer.toHexString(encodedCatchHandler.getOffsetInList())); - } - - out.writeInt(startCodeAddress); - out.writeShort(tryLength); - out.writeShort(encodedCatchHandler.getOffsetInList()); - } - - /** - * @return The address (in 2-byte words) within the code where the try block starts - */ - public int getStartCodeAddress() { - return startCodeAddress; - } - - /** - * @return The number of code blocks that the try block covers - */ - public int getTryLength() { - return tryLength; - } - } - - public static class EncodedCatchHandler { - /** - * An array of the individual exception handlers - */ - public final EncodedTypeAddrPair[] handlers; - - /** - * The address within the code (in 2-byte words) for the catch all handler, or -1 if there is no catch all - * handler - */ - private int catchAllHandlerAddress; - - private int baseOffset; - private int offset; - - /** - * Constructs a new EncodedCatchHandler with the given values - * @param handlers an array of the individual exception handlers - * @param catchAllHandlerAddress The address within the code (in 2-byte words) for the catch all handler, or -1 - * if there is no catch all handler - */ - public EncodedCatchHandler(EncodedTypeAddrPair[] handlers, int catchAllHandlerAddress) { - this.handlers = handlers; - this.catchAllHandlerAddress = catchAllHandlerAddress; - } - - /** - * This is used internally to construct a new EncodedCatchHandler while reading in a - * DexFile - * @param dexFile the DexFile that is being read in - * @param in the Input object to read the EncodedCatchHandler from - */ - private EncodedCatchHandler(DexFile dexFile, Input in) { - int handlerCount = in.readSignedLeb128(); - - if (handlerCount < 0) { - handlers = new EncodedTypeAddrPair[-1 * handlerCount]; - } else { - handlers = new EncodedTypeAddrPair[handlerCount]; - } - - for (int i=0; iEncodedCatchHandler, or -1 if there - * is no "Catch All" handler - */ - public int getCatchAllHandlerAddress() { - return catchAllHandlerAddress; - } - - /** - * @return the offset of this EncodedCatchHandler from the beginning of the - * encoded_catch_handler_list structure - */ - private int getOffsetInList() { - return offset-baseOffset; - } - - /** - * Places the EncodedCatchHandler, storing the offset and baseOffset, and returning the offset - * immediately following this EncodedCatchHandler - * @param offset the offset of this EncodedCatchHandler in the DexFile - * @param baseOffset the offset of the beginning of the encoded_catch_handler_list structure in the - * DexFile - * @return the offset immediately following this EncodedCatchHandler - */ - private int place(int offset, int baseOffset) { - this.offset = offset; - this.baseOffset = baseOffset; - - int size = handlers.length; - if (catchAllHandlerAddress > -1) { - size *= -1; - offset += Leb128Utils.unsignedLeb128Size(catchAllHandlerAddress); - } - offset += Leb128Utils.signedLeb128Size(size); - - for (EncodedTypeAddrPair handler: handlers) { - offset += handler.getSize(); - } - return offset; - } - - /** - * Writes the EncodedCatchHandler to the given AnnotatedOutput object - * @param out the AnnotatedOutput object to write to - */ - private void writeTo(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate("size: 0x" + Integer.toHexString(handlers.length) + " (" + handlers.length + ")"); - - int size = handlers.length; - if (catchAllHandlerAddress > -1) { - size = size * -1; - } - out.writeSignedLeb128(size); - - int index = 0; - for (EncodedTypeAddrPair handler: handlers) { - out.annotate(0, "[" + index++ + "] encoded_type_addr_pair"); - out.indent(); - handler.writeTo(out); - out.deindent(); - } - - if (catchAllHandlerAddress > -1) { - out.annotate("catch_all_addr: 0x" + Integer.toHexString(catchAllHandlerAddress)); - out.writeUnsignedLeb128(catchAllHandlerAddress); - } - } else { - int size = handlers.length; - if (catchAllHandlerAddress > -1) { - size = size * -1; - } - out.writeSignedLeb128(size); - - for (EncodedTypeAddrPair handler: handlers) { - handler.writeTo(out); - } - - if (catchAllHandlerAddress > -1) { - out.writeUnsignedLeb128(catchAllHandlerAddress); - } - } - } - - @Override - public int hashCode() { - int hash = 0; - for (EncodedTypeAddrPair handler: handlers) { - hash = hash * 31 + handler.hashCode(); - } - hash = hash * 31 + catchAllHandlerAddress; - return hash; - } - - @Override - public boolean equals(Object o) { - if (this==o) { - return true; - } - if (o==null || !this.getClass().equals(o.getClass())) { - return false; - } - - EncodedCatchHandler other = (EncodedCatchHandler)o; - if (handlers.length != other.handlers.length || catchAllHandlerAddress != other.catchAllHandlerAddress) { - return false; - } - - for (int i=0; iException that this handler handles - */ - public final TypeIdItem exceptionType; - - /** - * The address (in 2-byte words) in the code of the handler - */ - private int handlerAddress; - - /** - * Constructs a new EncodedTypeAddrPair with the given values - * @param exceptionType the type of the Exception that this handler handles - * @param handlerAddress the address (in 2-byte words) in the code of the handler - */ - public EncodedTypeAddrPair(TypeIdItem exceptionType, int handlerAddress) { - this.exceptionType = exceptionType; - this.handlerAddress = handlerAddress; - } - - /** - * This is used internally to construct a new EncodedTypeAddrPair while reading in a - * DexFile - * @param dexFile the DexFile that is being read in - * @param in the Input object to read the EncodedCatchHandler from - */ - private EncodedTypeAddrPair(DexFile dexFile, Input in) { - exceptionType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128()); - handlerAddress = in.readUnsignedLeb128(); - } - - /** - * @return the size of this EncodedTypeAddrPair - */ - private int getSize() { - return Leb128Utils.unsignedLeb128Size(exceptionType.getIndex()) + - Leb128Utils.unsignedLeb128Size(handlerAddress); - } - - /** - * Writes the EncodedTypeAddrPair to the given AnnotatedOutput object - * @param out the AnnotatedOutput object to write to - */ - private void writeTo(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate("exception_type: " + exceptionType.getTypeDescriptor()); - out.writeUnsignedLeb128(exceptionType.getIndex()); - - out.annotate("handler_addr: 0x" + Integer.toHexString(handlerAddress)); - out.writeUnsignedLeb128(handlerAddress); - } else { - out.writeUnsignedLeb128(exceptionType.getIndex()); - out.writeUnsignedLeb128(handlerAddress); - } - } - - public int getHandlerAddress() { - return handlerAddress; - } - - @Override - public int hashCode() { - return exceptionType.hashCode() * 31 + handlerAddress; - } - - @Override - public boolean equals(Object o) { - if (this==o) { - return true; - } - if (o==null || !this.getClass().equals(o.getClass())) { - return false; - } - - EncodedTypeAddrPair other = (EncodedTypeAddrPair)o; - return exceptionType == other.exceptionType && handlerAddress == other.handlerAddress; - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java deleted file mode 100644 index b61f399d..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugInstructionIterator.java +++ /dev/null @@ -1,364 +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.Debug; - -import org.jf.dexlib.DebugInfoItem; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.StringIdItem; -import org.jf.dexlib.TypeIdItem; -import org.jf.dexlib.Util.ByteArrayInput; -import org.jf.dexlib.Util.Input; - -public class DebugInstructionIterator { - /** - * This method decodes the debug instructions in the given byte array and iterates over them, calling - * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction - * @param in an Input object that the debug instructions can be read from - * @param processDebugInstruction a ProcessDebugInstructionDelegate object that gets called - * for each instruction that is encountered - */ - public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) { - int startDebugOffset; - - while(true) - { - startDebugOffset = in.getCursor(); - byte debugOpcode = in.readByte(); - - switch (debugOpcode) { - case 0x00: - { - processDebugInstruction.ProcessEndSequence(startDebugOffset); - return; - } - case 0x01: - { - int codeAddressDiff = in.readUnsignedLeb128(); - processDebugInstruction.ProcessAdvancePC(startDebugOffset, in.getCursor() - startDebugOffset, - codeAddressDiff); - break; - } - case 0x02: - { - int lineDiff = in.readSignedLeb128(); - processDebugInstruction.ProcessAdvanceLine(startDebugOffset, in.getCursor() - startDebugOffset, - lineDiff); - break; - } - case 0x03: - { - int registerNum = in.readUnsignedOrSignedLeb128(); - boolean isSignedRegister = false; - if (registerNum < 0) { - isSignedRegister = true; - registerNum = ~registerNum; - } - int nameIndex = in.readUnsignedLeb128() - 1; - int typeIndex = in.readUnsignedLeb128() - 1; - processDebugInstruction.ProcessStartLocal(startDebugOffset, in.getCursor() - startDebugOffset, - registerNum, nameIndex, typeIndex, isSignedRegister); - break; - } - case 0x04: - { - int registerNum = in.readUnsignedOrSignedLeb128(); - boolean isSignedRegister = false; - if (registerNum < 0) { - isSignedRegister = true; - registerNum = ~registerNum; - } - int nameIndex = in.readUnsignedLeb128() - 1; - int typeIndex = in.readUnsignedLeb128() - 1; - int signatureIndex = in.readUnsignedLeb128() - 1; - processDebugInstruction.ProcessStartLocalExtended(startDebugOffset, - in.getCursor() - startDebugOffset, registerNum, nameIndex, typeIndex, signatureIndex, - isSignedRegister); - break; - } - case 0x05: - { - int registerNum = in.readUnsignedOrSignedLeb128(); - boolean isSignedRegister = false; - if (registerNum < 0) { - isSignedRegister = true; - registerNum = ~registerNum; - } - processDebugInstruction.ProcessEndLocal(startDebugOffset, in.getCursor() - startDebugOffset, - registerNum, isSignedRegister); - break; - } - case 0x06: - { - int registerNum = in.readUnsignedOrSignedLeb128(); - boolean isSignedRegister = false; - if (registerNum < 0) { - isSignedRegister = true; - registerNum = ~registerNum; - } - processDebugInstruction.ProcessRestartLocal(startDebugOffset, in.getCursor() - startDebugOffset, - registerNum, isSignedRegister); - break; - } - case 0x07: - { - processDebugInstruction.ProcessSetPrologueEnd(startDebugOffset); - break; - } - case 0x08: - { - processDebugInstruction.ProcessSetEpilogueBegin(startDebugOffset); - break; - } - case 0x09: - { - int nameIndex = in.readUnsignedLeb128(); - processDebugInstruction.ProcessSetFile(startDebugOffset, in.getCursor() - startDebugOffset, - nameIndex); - break; - } - default: - { - int base = ((debugOpcode & 0xFF) - 0x0A); - processDebugInstruction.ProcessSpecialOpcode(startDebugOffset, debugOpcode, (base % 15) - 4, base / 15); - } - } - } - } - - /** - * This method decodes the debug instructions in the given byte array and iterates over them, calling - * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction - * @param debugInfoItem the DebugInfoItem to iterate over - * @param registerCount the number of registers in the method that the given debug info is for - * @param processDecodedDebugInstruction a ProcessDebugInstructionDelegate object that gets called - * for each instruction that is encountered - */ - public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount, - ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) { - int startDebugOffset; - int currentCodeAddress = 0; - int line = debugInfoItem.getLineStart(); - Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo()); - DexFile dexFile = debugInfoItem.getDexFile(); - - Local[] locals = new Local[registerCount]; - - while(true) - { - startDebugOffset = in.getCursor(); - byte debugOpcode = in.readByte(); - - switch (DebugOpcode.getDebugOpcodeByValue(debugOpcode)) { - case DBG_END_SEQUENCE: - { - return; - } - case DBG_ADVANCE_PC: - { - int codeAddressDiff = in.readUnsignedLeb128(); - currentCodeAddress += codeAddressDiff; - break; - } - case DBG_ADVANCE_LINE: - { - int lineDiff = in.readSignedLeb128(); - line += lineDiff; - break; - } - case DBG_START_LOCAL: - { - int registerNum = in.readUnsignedLeb128(); - StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); - TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); - locals[registerNum] = new Local(registerNum, name, type, null); - processDecodedDebugInstruction.ProcessStartLocal(currentCodeAddress, - in.getCursor() - startDebugOffset, registerNum, name, type); - break; - } - case DBG_START_LOCAL_EXTENDED: - { - int registerNum = in.readUnsignedLeb128(); - StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); - TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); - StringIdItem signature = - dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); - locals[registerNum] = new Local(registerNum, name, type, signature); - processDecodedDebugInstruction.ProcessStartLocalExtended(currentCodeAddress, - in.getCursor() - startDebugOffset, registerNum, name, type, signature); - break; - } - case DBG_END_LOCAL: - { - int registerNum = in.readUnsignedLeb128(); - Local local = locals[registerNum]; - if (local == null) { - processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, - null, null, null); - } else { - processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, - local.name, local.type, local.signature); - } - break; - } - case DBG_RESTART_LOCAL: - { - int registerNum = in.readUnsignedLeb128(); - Local local = locals[registerNum]; - if (local == null) { - processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, - registerNum, null, null, null); - } else { - processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, - registerNum, local.name, local.type, local.signature); - } - - break; - } - case DBG_SET_PROLOGUE_END: - { - processDecodedDebugInstruction.ProcessSetPrologueEnd(currentCodeAddress); - break; - } - case DBG_SET_EPILOGUE_BEGIN: - { - processDecodedDebugInstruction.ProcessSetEpilogueBegin(currentCodeAddress); - break; - } - case DBG_SET_FILE: - { - StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); - processDecodedDebugInstruction.ProcessSetFile(currentCodeAddress, in.getCursor() - startDebugOffset, name); - break; - } - case DBG_SPECIAL_OPCODE: - { - int base = ((debugOpcode & 0xFF) - 0x0A); - currentCodeAddress += base / 15; - line += (base % 15) - 4; - processDecodedDebugInstruction.ProcessLineEmit(currentCodeAddress, line); - } - } - } - } - - public static class ProcessRawDebugInstructionDelegate - { - //TODO: add javadocs - public void ProcessEndSequence(int startDebugOffset) { - ProcessStaticOpcode(DebugOpcode.DBG_END_SEQUENCE, startDebugOffset, 1); - } - - public void ProcessAdvancePC(int startDebugOffset, int length, int codeAddressDiff) { - ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_PC, startDebugOffset, length); - } - - public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) { - ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_LINE, startDebugOffset, length); - } - - public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, int typeIndex, - boolean registerIsSigned) { - } - - public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, - int typeIndex,int signatureIndex, boolean registerIsSigned) { - } - - public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { - ProcessStaticOpcode(DebugOpcode.DBG_END_LOCAL, startDebugOffset, length); - } - - public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { - ProcessStaticOpcode(DebugOpcode.DBG_RESTART_LOCAL, startDebugOffset, length); - } - - public void ProcessSetPrologueEnd(int startDebugOffset) { - ProcessStaticOpcode(DebugOpcode.DBG_SET_PROLOGUE_END, startDebugOffset, 1); - } - - public void ProcessSetEpilogueBegin(int startDebugOffset) { - ProcessStaticOpcode(DebugOpcode.DBG_SET_EPILOGUE_BEGIN, startDebugOffset, 1); - } - - public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { - } - - public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, int codeAddressDiff) { - ProcessStaticOpcode(DebugOpcode.DBG_SPECIAL_OPCODE, startDebugOffset, 1); - } - - public void ProcessStaticOpcode(DebugOpcode debugOpcode, int startDebugOffset, int length) { - } - } - - public static class ProcessDecodedDebugInstructionDelegate - { - public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name, - TypeIdItem type) { - } - - public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name, - TypeIdItem type, StringIdItem signature) { - } - - public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type, - StringIdItem signature) { - } - - public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name, - TypeIdItem type, StringIdItem signature) { - } - - public void ProcessSetPrologueEnd(int codeAddress) { - } - - public void ProcessSetEpilogueBegin(int codeAddress) { - } - - public void ProcessSetFile(int codeAddress, int length, StringIdItem name) { - } - - public void ProcessLineEmit(int codeAddress, int line) { - } - } - - private static class Local { - public final int register; - public final StringIdItem name; - public final TypeIdItem type; - public final StringIdItem signature; - public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) { - this.register = register; - this.name = name; - this.type = type; - this.signature = signature; - } - - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java deleted file mode 100644 index d219ee33..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Debug/DebugOpcode.java +++ /dev/null @@ -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.Debug; - -public enum DebugOpcode { - DBG_END_SEQUENCE((byte)0x00), - DBG_ADVANCE_PC((byte)0x01), - DBG_ADVANCE_LINE((byte)0x02), - DBG_START_LOCAL((byte)0x03), - DBG_START_LOCAL_EXTENDED((byte)0x04), - DBG_END_LOCAL((byte)0x05), - DBG_RESTART_LOCAL((byte)0x06), - DBG_SET_PROLOGUE_END((byte)0x07), - DBG_SET_EPILOGUE_BEGIN((byte)0x08), - DBG_SET_FILE((byte)0x09), - DBG_SPECIAL_OPCODE((byte)0x0A); - - private static DebugOpcode[] opcodesByValue; - - static { - opcodesByValue = new DebugOpcode[11]; - - for (DebugOpcode debugOpcode: DebugOpcode.values()) { - opcodesByValue[debugOpcode.value & 0xFF] = debugOpcode; - } - } - - public static DebugOpcode getDebugOpcodeByValue(byte debugOpcodeValue) { - debugOpcodeValue = (byte)Math.min(debugOpcodeValue & 0xFF, 0x0A); - return opcodesByValue[debugOpcodeValue]; - } - - public final byte value; - - DebugOpcode(byte value) { - this.value = value; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java deleted file mode 100644 index 81e023de..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java +++ /dev/null @@ -1,620 +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.Debug.DebugInstructionIterator; -import org.jf.dexlib.Debug.DebugOpcode; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.ByteArrayInput; -import org.jf.dexlib.Util.Input; -import org.jf.dexlib.Util.Leb128Utils; - -import java.util.ArrayList; -import java.util.List; - -public class DebugInfoItem extends Item { - private int lineStart; - private StringIdItem[] parameterNames; - private byte[] encodedDebugInfo; - private Item[] referencedItems; - - private CodeItem parent = null; - - /** - * Creates a new uninitialized DebugInfoInfo - * @param dexFile The DexFile that this item belongs to - */ - public DebugInfoItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new DebugInfoItem with the given values - * @param dexFile The DexFile that this item belongs to - * @param lineStart the initial value for the line number register for the debug info machine - * @param parameterNames an array of the names of the associated method's parameters. The entire parameter - * can be null if no parameter info is available, or any element can be null to indicate no info for that parameter - * @param encodedDebugInfo the debug info, encoded as a byte array - * @param referencedItems an array of the items referenced by instructions, in order of occurance in the encoded - * debug info - */ - private DebugInfoItem(DexFile dexFile, - int lineStart, - StringIdItem[] parameterNames, - byte[] encodedDebugInfo, - Item[] referencedItems) { - super(dexFile); - this.lineStart = lineStart; - this.parameterNames = parameterNames; - this.encodedDebugInfo = encodedDebugInfo; - this.referencedItems = referencedItems; - } - - /** - * Returns a new DebugInfoItem with the given values - * @param dexFile The DexFile that this item belongs to - * @param lineStart the initial value for the line number register for the debug info machine - * @param parameterNames an array of the names of the associated method's parameters. The entire parameter - * can be null if no parameter info is available, or any element can be null to indicate no info for that parameter - * @param encodedDebugInfo the debug info, encoded as a byte array - * @param referencedItems an array of the items referenced by instructions, in order of occurance in the encoded - * debug info - * @return a new DebugInfoItem with the given values - */ - public static DebugInfoItem internDebugInfoItem(DexFile dexFile, - int lineStart, - StringIdItem[] parameterNames, - byte[] encodedDebugInfo, - Item[] referencedItems) { - DebugInfoItem debugInfoItem = new DebugInfoItem(dexFile, lineStart, parameterNames, encodedDebugInfo, - referencedItems); - return dexFile.DebugInfoItemsSection.intern(debugInfoItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - lineStart = in.readUnsignedLeb128(); - parameterNames = new StringIdItem[in.readUnsignedLeb128()]; - IndexedSection stringIdSection = dexFile.StringIdsSection; - for (int i=0; i referencedItemsList = new ArrayList(50); - DebugInstructionIterator.IterateInstructions(in, - new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() { - @Override - public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, - int typeIndex, boolean registerIsSigned) { - if (nameIndex != -1) { - referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); - } - if (typeIndex != -1) { - referencedItemsList.add(dexFile.TypeIdsSection.getItemByIndex(typeIndex)); - } - } - - @Override - public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNume, - int nameIndex, int typeIndex, int signatureIndex, - boolean registerIsSigned) { - if (nameIndex != -1) { - referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); - } - if (typeIndex != -1) { - referencedItemsList.add(dexFile.TypeIdsSection.getItemByIndex(typeIndex)); - } - if (signatureIndex != -1) { - referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(signatureIndex)); - } - } - - @Override - public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { - if (nameIndex != -1) { - referencedItemsList.add(dexFile.StringIdsSection.getItemByIndex(nameIndex)); - } - } - }); - - referencedItems = new Item[referencedItemsList.size()]; - referencedItemsList.toArray(referencedItems); - - int length = in.getCursor() - start; - in.setCursor(start); - encodedDebugInfo = in.readBytes(length); - } - - - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - offset += Leb128Utils.unsignedLeb128Size(lineStart); - offset += Leb128Utils.unsignedLeb128Size(parameterNames.length); - for (StringIdItem parameterName: parameterNames) { - int indexp1; - if (parameterName == null) { - indexp1 = 0; - } else { - indexp1 = parameterName.getIndex() + 1; - } - offset += Leb128Utils.unsignedLeb128Size(indexp1); - } - - //make a subclass so we can keep track of and access the computed length - class ProcessDebugInstructionDelegateWithLength extends - DebugInstructionIterator.ProcessRawDebugInstructionDelegate { - public int length = 0; - } - ProcessDebugInstructionDelegateWithLength pdidwl; - - //final referencedItems = this.referencedItems; - - DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo), - pdidwl = new ProcessDebugInstructionDelegateWithLength() { - private int referencedItemsPosition = 0; - - @Override - public void ProcessStaticOpcode(DebugOpcode opcode, int startDebugOffset, int length) { - this.length+=length; - } - - @Override - public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, - int typeIndex, boolean registerIsSigned) { - this.length++; - if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { - this.length += Leb128Utils.signedLeb128Size(registerNum); - } else { - this.length+=Leb128Utils.unsignedLeb128Size(registerNum); - } - if (nameIndex != -1) { - this.length+= - Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); - } else { - this.length++; - } - if (typeIndex != -1) { - this.length+= - Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); - } else { - this.length++; - } - - } - - @Override - public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, - int typeIndex, int signatureIndex, - boolean registerIsSigned) { - this.length++; - if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { - this.length += Leb128Utils.signedLeb128Size(registerNum); - } else { - this.length+=Leb128Utils.unsignedLeb128Size(registerNum); - } - if (nameIndex != -1) { - this.length+= - Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); - } else { - this.length++; - } - if (typeIndex != -1) { - this.length+= - Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); - } else { - this.length++; - } - if (signatureIndex != -1) { - this.length+= - Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); - } else { - this.length++; - } - } - - @Override - public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { - this.length++; - if (nameIndex != -1) { - this.length+= - Leb128Utils.unsignedLeb128Size(referencedItems[referencedItemsPosition++].getIndex()+1); - } else { - this.length++; - } - } - }); - return offset + pdidwl.length; - } - - /** {@inheritDoc} */ - protected void writeItem(final AnnotatedOutput out) { - if (out.annotates()) { - writeItemWithAnnotations(out); - } else { - writeItemWithNoAnnotations(out); - } - } - - /** - * Replaces the encoded debug info for this DebugInfoItem. It is expected that the new debug info is compatible - * with the existing information, i.e. lineStart, referencedItems, parameterNames - * @param encodedDebugInfo the new encoded debug info - */ - protected void setEncodedDebugInfo(byte[] encodedDebugInfo) { - //TODO: I would rather replace this method with some way of saying "The (code) instruction at address changed from A bytes to B bytes. Fixup the debug info accordingly" - - this.encodedDebugInfo = encodedDebugInfo; - } - - /** - * Helper method that writes the item, without writing annotations - * @param out the AnnotatedOutput object - */ - private void writeItemWithNoAnnotations(final AnnotatedOutput out) { - out.writeUnsignedLeb128(lineStart); - out.writeUnsignedLeb128(parameterNames.length); - for (StringIdItem parameterName: parameterNames) { - int indexp1; - if (parameterName == null) { - indexp1 = 0; - } else { - indexp1 = parameterName.getIndex() + 1; - } - out.writeUnsignedLeb128(indexp1); - } - - DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo), - new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() { - private int referencedItemsPosition = 0; - - @Override - public void ProcessStaticOpcode(DebugOpcode opcode, int startDebugOffset, int length) { - out.write(encodedDebugInfo, startDebugOffset, length); - } - - @Override - public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, - int typeIndex, boolean registerIsSigned) { - out.writeByte(DebugOpcode.DBG_START_LOCAL.value); - if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { - out.writeSignedLeb128(registerNum); - } else { - out.writeUnsignedLeb128(registerNum); - } - if (nameIndex != -1) { - out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); - } else { - out.writeByte(0); - } - if (typeIndex != -1) { - out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); - } else { - out.writeByte(0); - } - } - - @Override - public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, - int typeIndex, int signatureIndex, - boolean registerIsSigned) { - out.writeByte(DebugOpcode.DBG_START_LOCAL_EXTENDED.value); - if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { - out.writeSignedLeb128(registerNum); - } else { - out.writeUnsignedLeb128(registerNum); - } - if (nameIndex != -1) { - out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); - } else { - out.writeByte(0); - } - if (typeIndex != -1) { - out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); - } else { - out.writeByte(0); - } - if (signatureIndex != -1) { - out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); - } else { - out.writeByte(0); - } - } - - @Override - public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { - out.writeByte(DebugOpcode.DBG_SET_FILE.value); - if (nameIndex != -1) { - out.writeUnsignedLeb128(referencedItems[referencedItemsPosition++].getIndex() + 1); - } else { - out.writeByte(0); - } - } - }); - } - - /** - * Helper method that writes and annotates the item - * @param out the AnnotatedOutput object - */ - private void writeItemWithAnnotations(final AnnotatedOutput out) { - out.annotate(0, parent.getParent().method.getMethodString()); - out.annotate("line_start: 0x" + Integer.toHexString(lineStart) + " (" + lineStart + ")"); - out.writeUnsignedLeb128(lineStart); - out.annotate("parameters_size: 0x" + Integer.toHexString(parameterNames.length) + " (" + parameterNames.length - + ")"); - out.writeUnsignedLeb128(parameterNames.length); - int index = 0; - for (StringIdItem parameterName: parameterNames) { - int indexp1; - if (parameterName == null) { - out.annotate("[" + index++ +"] parameterName: "); - indexp1 = 0; - } else { - out.annotate("[" + index++ +"] parameterName: " + parameterName.getStringValue()); - indexp1 = parameterName.getIndex() + 1; - } - out.writeUnsignedLeb128(indexp1); - } - - DebugInstructionIterator.IterateInstructions(new ByteArrayInput(encodedDebugInfo), - new DebugInstructionIterator.ProcessRawDebugInstructionDelegate() { - private int referencedItemsPosition = 0; - - @Override - public void ProcessEndSequence(int startDebugOffset) { - out.annotate("DBG_END_SEQUENCE"); - out.writeByte(DebugOpcode.DBG_END_SEQUENCE.value); - } - - @Override - public void ProcessAdvancePC(int startDebugOffset, int length, int addressDiff) { - out.annotate("DBG_ADVANCE_PC"); - out.writeByte(DebugOpcode.DBG_ADVANCE_PC.value); - out.indent(); - out.annotate("addr_diff: 0x" + Integer.toHexString(addressDiff) + " (" + addressDiff + ")"); - out.writeUnsignedLeb128(addressDiff); - out.deindent(); - } - - @Override - public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) { - out.annotate("DBG_ADVANCE_LINE"); - out.writeByte(DebugOpcode.DBG_ADVANCE_LINE.value); - out.indent(); - out.annotate("line_diff: 0x" + Integer.toHexString(lineDiff) + " (" + lineDiff + ")"); - out.writeSignedLeb128(lineDiff); - out.deindent(); - } - - @Override - public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, - int typeIndex, boolean registerIsSigned) { - out.annotate("DBG_START_LOCAL"); - out.writeByte(DebugOpcode.DBG_START_LOCAL.value); - out.indent(); - out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")"); - if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { - out.writeSignedLeb128(registerNum); - } else { - out.writeUnsignedLeb128(registerNum); - } - if (nameIndex != -1) { - Item nameItem = referencedItems[referencedItemsPosition++]; - assert nameItem instanceof StringIdItem; - out.annotate("name: " + ((StringIdItem)nameItem).getStringValue()); - out.writeUnsignedLeb128(nameItem.getIndex() + 1); - } else { - out.annotate("name: "); - out.writeByte(0); - } - if (typeIndex != -1) { - Item typeItem = referencedItems[referencedItemsPosition++]; - assert typeItem instanceof TypeIdItem; - out.annotate("type: " + ((TypeIdItem)typeItem).getTypeDescriptor()); - out.writeUnsignedLeb128(typeItem.getIndex() + 1); - } else { - out.annotate("type: "); - out.writeByte(0); - } - out.deindent(); - } - - @Override - public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, - int nameIndex, int typeIndex, int signatureIndex, - boolean registerIsSigned) { - out.annotate("DBG_START_LOCAL_EXTENDED"); - out.writeByte(DebugOpcode.DBG_START_LOCAL_EXTENDED.value); - out.indent(); - out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")"); - if (dexFile.getPreserveSignedRegisters() && registerIsSigned) { - out.writeSignedLeb128(registerNum); - } else { - out.writeUnsignedLeb128(registerNum); - } - if (nameIndex != -1) { - Item nameItem = referencedItems[referencedItemsPosition++]; - assert nameItem instanceof StringIdItem; - out.annotate("name: " + ((StringIdItem)nameItem).getStringValue()); - out.writeUnsignedLeb128(nameItem.getIndex() + 1); - } else { - out.annotate("name: "); - out.writeByte(0); - } - if (typeIndex != -1) { - Item typeItem = referencedItems[referencedItemsPosition++]; - assert typeItem instanceof TypeIdItem; - out.annotate("type: " + ((TypeIdItem)typeItem).getTypeDescriptor()); - out.writeUnsignedLeb128(typeItem.getIndex() + 1); - } else { - out.annotate("type: "); - out.writeByte(0); - } - if (signatureIndex != -1) { - Item signatureItem = referencedItems[referencedItemsPosition++]; - assert signatureItem instanceof StringIdItem; - out.annotate("signature: " + ((StringIdItem)signatureItem).getStringValue()); - out.writeUnsignedLeb128(signatureItem.getIndex() + 1); - } else { - out.annotate("signature: "); - out.writeByte(0); - } - out.deindent(); - } - - @Override - public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, - boolean registerIsSigned) { - out.annotate("DBG_END_LOCAL"); - out.writeByte(DebugOpcode.DBG_END_LOCAL.value); - out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")"); - if (registerIsSigned) { - out.writeSignedLeb128(registerNum); - } else { - out.writeUnsignedLeb128(registerNum); - } - } - - @Override - public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, - boolean registerIsSigned) { - out.annotate("DBG_RESTART_LOCAL"); - out.writeByte(DebugOpcode.DBG_RESTART_LOCAL.value); - out.annotate("register_num: 0x" + Integer.toHexString(registerNum) + " (" + registerNum + ")"); - if (registerIsSigned) { - out.writeSignedLeb128(registerNum); - } else { - out.writeUnsignedLeb128(registerNum); - } - } - - @Override - public void ProcessSetPrologueEnd(int startDebugOffset) { - out.annotate("DBG_SET_PROLOGUE_END"); - out.writeByte(DebugOpcode.DBG_SET_PROLOGUE_END.value); - } - - @Override - public void ProcessSetEpilogueBegin(int startDebugOffset) { - out.annotate("DBG_SET_EPILOGUE_BEGIN"); - out.writeByte(DebugOpcode.DBG_SET_EPILOGUE_BEGIN.value); - } - - @Override - public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { - out.annotate("DBG_SET_FILE"); - out.writeByte(DebugOpcode.DBG_SET_FILE.value); - if (nameIndex != -1) { - Item sourceItem = referencedItems[referencedItemsPosition++]; - assert sourceItem instanceof StringIdItem; - out.annotate("source_file: \"" + ((StringIdItem)sourceItem).getStringValue() + "\""); - out.writeUnsignedLeb128(sourceItem.getIndex() + 1); - } else { - out.annotate("source_file: "); - out.writeByte(0); - } - } - - @Override - public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, - int addressDiff) { - out.annotate("DBG_SPECIAL_OPCODE: line_diff=0x" + Integer.toHexString(lineDiff) + "(" + - lineDiff +"),addressDiff=0x" + Integer.toHexString(addressDiff) + "(" + addressDiff + - ")"); - out.writeByte(debugOpcode); - } - }); - } - - - - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_DEBUG_INFO_ITEM; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "debug_info_item @0x" + Integer.toHexString(getOffset()); - } - - /** {@inheritDoc} */ - public int compareTo(DebugInfoItem other) { - if (parent == null) { - if (other.parent == null) { - return 0; - } - return -1; - } - if (other.parent == null) { - return 1; - } - return parent.compareTo(other.parent); - } - - /** - * Set the CodeItem that this DebugInfoItem is associated with - * @param codeItem the CodeItem that this DebugInfoItem is associated with - */ - protected void setParent(CodeItem codeItem) { - this.parent = codeItem; - } - - /** - * @return the initial value for the line number register for the debug info machine - */ - public int getLineStart() { - return lineStart; - } - - /** - * @return the debug info, encoded as a byte array - */ - public byte[] getEncodedDebugInfo() { - return encodedDebugInfo; - } - - /** - * @return an array of the items referenced by instructions, in order of occurance in the encoded debug info - */ - public Item[] getReferencedItems() { - return referencedItems; - } - - /** - * @return an array of the names of the associated method's parameters. The array can be null if no parameter info - * is available, or any element can be null to indicate no info for that parameter - */ - public StringIdItem[] getParameterNames() { - return parameterNames; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DexFile.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DexFile.java deleted file mode 100644 index 80f25f87..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/DexFile.java +++ /dev/null @@ -1,901 +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.*; - -import java.io.*; -import java.security.DigestException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.zip.Adler32; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - *

Main use cases

- * - *

These are the main use cases that drove the design of this library

- * - *
    - *
  1. Annotate an existing dex file - In this case, the intent is to document the structure of - * an existing dex file. We want to be able to read in the dex file, and then write out a dex file - * that is exactly the same (while adding annotation information to an AnnotatedOutput object)

  2. - * - *
  3. Canonicalize an existing dex file - In this case, the intent is to rewrite an existing dex file - * so that it is in a canonical form. There is a certain amount of leeway in how various types of - * tems in a dex file are ordered or represented. It is sometimes useful to be able to easily - * compare a disassebled and reassembled dex file with the original dex file. If both dex-files are - * written canonically, they "should" match exactly, barring any explicit changes to the reassembled - * file.

    - * - *

    Currently, there are a couple of pieces of information that probably won't match exactly - *

      - *
    • the order of exception handlers in the EncodedCatchHandlerList for a method
    • - *
    • the ordering of some of the debug info in the {@link org.jf.dexlib.DebugInfoItem} for a method
    • - *

    - * - * - *

    Note that the above discrepancies should typically only be "intra-item" differences. They - * shouldn't change the size of the item, or affect how anything else is placed or laid out

  4. - * - *
  5. Creating a dex file from scratch - In this case, a blank dex file is created and then classes - * are added to it incrementally by calling the {@link org.jf.dexlib.Section#intern intern} method of - * {@link DexFile#ClassDefsSection}, which will add all the information necessary to represent the given - * class. For example, when assembling a dex file from a set of assembly text files.

    - * - *

    In this case, we can choose to write the dex file in a canonical form or not. It is somewhat - * slower to write it in a canonical format, due to the extra sorting and calculations that are - * required.

  6. - * - * - *
  7. Reading in the dex file - In this case, the intent is to read in a dex file and expose all the - * data to the calling application. For example, when disassembling a dex file into a text based - * assembly format, or doing other misc processing of the dex file.

  8. - * - * - *

    Other use cases

    - * - *

    These are other use cases that are possible, but did not drive the design of the library. - * No effort was made to test these use cases or ensure that they work. Some of these could - * probably be better achieved with a disassemble - modify - reassemble type process, using - * smali/baksmali or another assembler/disassembler pair that are compatible with each other

    - * - *
      - *
    • deleting classes/methods/etc. from a dex file
    • - *
    • merging 2 dex files
    • - *
    • splitting a dex file
    • - *
    • moving classes from 1 dex file to another
    • - *
    • removing the debug information from a dex file
    • - *
    • obfustication of a dex file
    • - *
    - */ -public class DexFile -{ - /** - * A mapping from ItemType to the section that contains items of the given type - */ - private final Section[] sectionsByType; - - /** - * Ordered lists of the indexed and offsetted sections. The order of these lists specifies the order - * that the sections will be written in - */ - private final IndexedSection[] indexedSections; - private final OffsettedSection[] offsettedSections; - - /** - * dalvik had a bug where it wrote the registers for certain types of debug info in a signed leb - * format, instead of an unsigned leb format. There are no negative registers of course, but - * certain positive values have a different encoding depending on whether they are encoded as - * an unsigned leb128 or a signed leb128. Specifically, the signed leb128 is 1 byte longer in some cases. - * - * This determine whether we should keep any signed registers as signed, or force all register to - * unsigned. By default we don't keep track of whether they were signed or not, and write them back - * out as unsigned. This option only has an effect when reading an existing dex file. It has no - * effect when a dex file is created from scratch - * - * The 2 main use-cases in play are - * 1. Annotate an existing dex file - In this case, preserveSignedRegisters should be false, so that we keep - * track of any signed registers and write them back out as signed Leb128 values. - * - * 2. Canonicalize an existing dex file - In this case, fixRegisters should be true, so that all - * registers in the debug info are written as unsigned Leb128 values regardless of how they were - * originally encoded - */ - private final boolean preserveSignedRegisters; - - /** - * When true, any instructions in a code item are skipped over instead of being read in. This is useful when - * you only need the information about the classes and their methods, for example, when loading the BOOTCLASSPATH - * jars in order to analyze a dex file - */ - private final boolean skipInstructions; - - /** - * When true, this prevents any sorting of the items during placement of the dex file. This - * should *only* be set to true when this dex file was read in from an existing (valid) dex file, - * and no modifications were made (i.e. no items added or deleted). Otherwise it is likely that - * an invalid dex file will be generated. - * - * This is useful for the first use case (annotating an existing dex file). This ensures the items - * retain the same order as in the original dex file. - */ - private boolean inplace = false; - - /** - * When true, this imposes an full ordering on all the items, to force them into a (possibly - * arbitrary) canonical order. When false, only the items that the dex format specifies - * an order for are sorted. The rest of the items are not ordered. - * - * This is useful for the second use case (canonicalizing an existing dex file) or possibly for - * the third use case (creating a dex file from scratch), if there is a need to write the new - * dex file in a canonical form. - */ - private boolean sortAllItems = false; - - /** - * Is this file an odex file? This is only set when reading in an odex file - */ - private boolean isOdex = false; - - private OdexHeader odexHeader; - private OdexDependencies odexDependencies; - - private int dataOffset; - private int dataSize; - private int fileSize; - - /** - * A private constructor containing common code to initialize the section maps and lists - * @param preserveSignedRegisters If true, keep track of any registers in the debug information - * @param skipInstructions If true, skip the instructions in any code item. - * that are signed, so they will be written in the same format. See - * getPreserveSignedRegisters() - */ - private DexFile(boolean preserveSignedRegisters, boolean skipInstructions) { - this.preserveSignedRegisters = preserveSignedRegisters; - this.skipInstructions = skipInstructions; - - sectionsByType = new Section[] { - StringIdsSection, - TypeIdsSection, - ProtoIdsSection, - FieldIdsSection, - MethodIdsSection, - ClassDefsSection, - TypeListsSection, - AnnotationSetRefListsSection, - AnnotationSetsSection, - ClassDataSection, - CodeItemsSection, - AnnotationDirectoriesSection, - StringDataSection, - DebugInfoItemsSection, - AnnotationsSection, - EncodedArraysSection, - null, - null - }; - - indexedSections = new IndexedSection[] { - StringIdsSection, - TypeIdsSection, - ProtoIdsSection, - FieldIdsSection, - MethodIdsSection, - ClassDefsSection - }; - - offsettedSections = new OffsettedSection[] { - AnnotationSetRefListsSection, - AnnotationSetsSection, - CodeItemsSection, - AnnotationDirectoriesSection, - TypeListsSection, - StringDataSection, - AnnotationsSection, - EncodedArraysSection, - ClassDataSection, - DebugInfoItemsSection - }; - } - - - /** - * Construct a new DexFile instance by reading in the given dex file. - * @param file The dex file to read in - * @throws IOException if an IOException occurs - */ - public DexFile(String file) - throws IOException { - this(new File(file), true, false); - } - - /** - * Construct a new DexFile instance by reading in the given dex file, - * and optionally keep track of any registers in the debug information that are signed, - * so they will be written in the same format. - * @param file The dex file to read in - * @param preserveSignedRegisters If true, keep track of any registers in the debug information - * that are signed, so they will be written in the same format. See - * @param skipInstructions If true, skip the instructions in any code item. - * getPreserveSignedRegisters() - * @throws IOException if an IOException occurs - */ - public DexFile(String file, boolean preserveSignedRegisters, boolean skipInstructions) - throws IOException { - this(new File(file), preserveSignedRegisters, skipInstructions); - } - - /** - * Construct a new DexFile instance by reading in the given dex file. - * @param file The dex file to read in - * @throws IOException if an IOException occurs - */ - public DexFile(File file) - throws IOException { - this(file, true, false); - } - - /** - * Construct a new DexFile instance by reading in the given dex file, - * and optionally keep track of any registers in the debug information that are signed, - * so they will be written in the same format. - * @param file The dex file to read in - * @param preserveSignedRegisters If true, keep track of any registers in the debug information - * that are signed, so they will be written in the same format. - * @param skipInstructions If true, skip the instructions in any code item. - * @see #getPreserveSignedRegisters - * @throws IOException if an IOException occurs - */ - public DexFile(File file, boolean preserveSignedRegisters, boolean skipInstructions) - throws IOException { - this(preserveSignedRegisters, skipInstructions); - - long fileLength; - byte[] magic = FileUtils.readFile(file, 0, 8); - - InputStream inputStream = null; - Input in = null; - ZipFile zipFile = null; - - try { - //do we have a zip file? - if (magic[0] == 0x50 && magic[1] == 0x4B) { - zipFile = new ZipFile(file); - ZipEntry zipEntry = zipFile.getEntry("classes.dex"); - if (zipEntry == null) { - throw new NoClassesDexException("zip file " + file.getName() + " does not contain a classes.dex " + - "file"); - } - fileLength = zipEntry.getSize(); - if (fileLength < 40) { - throw new RuntimeException("The classes.dex file in " + file.getName() + " is too small to be a" + - " valid dex file"); - } else if (fileLength > Integer.MAX_VALUE) { - throw new RuntimeException("The classes.dex file in " + file.getName() + " is too large to read in"); - } - inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry)); - - inputStream.mark(8); - for (int i=0; i<8; i++) { - magic[i] = (byte)inputStream.read(); - } - inputStream.reset(); - } else { - fileLength = file.length(); - if (fileLength < 40) { - throw new RuntimeException(file.getName() + " is too small to be a valid dex file"); - } - if (fileLength < 40) { - throw new RuntimeException(file.getName() + " is too small to be a valid dex file"); - } else if (fileLength > Integer.MAX_VALUE) { - throw new RuntimeException(file.getName() + " is too large to read in"); - } - inputStream = new FileInputStream(file); - } - - byte[] dexMagic, odexMagic; - boolean isDex = false; - this.isOdex = false; - - for (int i=0; i 40) { - FileUtils.readStream(inputStream, odexHeader.dexOffset - 40); - } - - in = new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.dexLength)); - - if (dependencySkip > 0) { - FileUtils.readStream(inputStream, dependencySkip); - } - - odexDependencies = new OdexDependencies( - new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.depsLength))); - } else if (isDex) { - in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength)); - } else { - StringBuffer sb = new StringBuffer("bad magic value:"); - for (int i=0; i<8; i++) { - sb.append(" "); - sb.append(Hex.u1(magic[i])); - } - throw new RuntimeException(sb.toString()); - } - } finally { - if (inputStream != null) { - inputStream.close(); - } - if (zipFile != null) { - zipFile.close(); - } - } - - ReadContext readContext = new ReadContext(); - - HeaderItem.readFrom(in, 0, readContext); - - //the map offset was set while reading in the header item - int mapOffset = readContext.getSectionOffset(ItemType.TYPE_MAP_LIST); - - in.setCursor(mapOffset); - MapItem.readFrom(in, 0, readContext); - - //the sections are ordered in such a way that the item types - Section sections[] = new Section[] { - StringDataSection, - StringIdsSection, - TypeIdsSection, - TypeListsSection, - ProtoIdsSection, - FieldIdsSection, - MethodIdsSection, - AnnotationsSection, - AnnotationSetsSection, - AnnotationSetRefListsSection, - AnnotationDirectoriesSection, - DebugInfoItemsSection, - CodeItemsSection, - ClassDataSection, - EncodedArraysSection, - ClassDefsSection - }; - - for (Section section: sections) { - if (section == null) { - continue; - } - - if (skipInstructions && (section == CodeItemsSection || section == DebugInfoItemsSection)) { - continue; - } - - int sectionOffset = readContext.getSectionOffset(section.ItemType); - if (sectionOffset > 0) { - int sectionSize = readContext.getSectionSize(section.ItemType); - in.setCursor(sectionOffset); - section.readFrom(sectionSize, in, readContext); - } - } - } - - /** - * Constructs a new, blank dex file. Classes can be added to this dex file by calling - * the Section.intern() method of ClassDefsSection - */ - public DexFile() { - this(true, false); - } - - /** - * Get the Section containing items of the same type as the given item - * @param item Get the Section that contains items of this type - * @param The specific item subclass - inferred from the passed item - * @return the Section containing items of the same type as the given item - */ - public Section getSectionForItem(T item) { - return (Section)sectionsByType[item.getItemType().SectionIndex]; - } - - /** - * Get the Section containing items of the given type - * @param itemType the type of item - * @return the Section containing items of the given type - */ - public Section getSectionForType(ItemType itemType) { - return sectionsByType[itemType.SectionIndex]; - } - - /** - * Get a boolean value indicating whether this dex file preserved any signed - * registers in the debug info as it read the dex file in. By default, the dex file - * doesn't check whether the registers are encoded as unsigned or signed values. - * - * This does *not* affect the actual register value that is read in. The value is - * read correctly regardless - * - * This does affect whether any signed registers will retain the same encoding or be - * forced to the (correct) unsigned encoding when the dex file is written back out. - * - * See the discussion about signed register values in the documentation for - * DexFile - * @return a boolean indicating whether this dex file preserved any signed registers - * as it was read in - */ - public boolean getPreserveSignedRegisters() { - return preserveSignedRegisters; - } - - /** - * Get a boolean value indicating whether to skip any instructions in a code item while reading in the dex file. - * This is useful when you only need the information about the classes and their methods, for example, when - * loading the BOOTCLASSPATH jars in order to analyze a dex file - * @return a boolean value indicating whether to skip any instructions in a code item - */ - public boolean skipInstructions() { - return skipInstructions; - } - - /** - * Get a boolean value indicating whether all items should be placed into a - * (possibly arbitrary) "canonical" ordering. If false, then only the items - * that must be ordered per the dex specification are sorted. - * - * When true, writing the dex file involves somewhat more overhead - * - * If both SortAllItems and Inplace are true, Inplace takes precedence - * @return a boolean value indicating whether all items should be sorted - */ - public boolean getSortAllItems() { - return this.sortAllItems; - } - - /** - * Set a boolean value indicating whether all items should be placed into a - * (possibly arbitrary) "canonical" ordering. If false, then only the items - * that must be ordered per the dex specification are sorted. - * - * When true, writing the dex file involves somewhat more overhead - * - * If both SortAllItems and Inplace are true, Inplace takes precedence - * @param value a boolean value indicating whether all items should be sorted - */ - public void setSortAllItems(boolean value) { - this.sortAllItems = value; - } - - /** - * @return a boolean value indicating whether this dex file was created by reading in an odex file - */ - public boolean isOdex() { - return this.isOdex; - } - - /** - * @return an OdexDependencies object that contains the dependencies for this odex, or null if this - * DexFile represents a dex file instead of an odex file - */ - public OdexDependencies getOdexDependencies() { - return odexDependencies; - } - - /** - * @return An OdexHeader object containing the information from the odex header in this dex file, or null if there - * is no odex header - */ - public OdexHeader getOdexHeader() { - return odexHeader; - } - - /** - * Get a boolean value indicating whether items in this dex file should be - * written back out "in-place", or whether the normal layout logic should be - * applied. - * - * This should only be used for a dex file that has been read from an existing - * dex file, and no modifications have been made to the dex file. Otherwise, - * there is a good chance that the resulting dex file will be invalid due to - * items that aren't placed correctly - * - * If both SortAllItems and Inplace are true, Inplace takes precedence - * @return a boolean value indicating whether items in this dex file should be - * written back out in-place. - */ - public boolean getInplace() { - return this.inplace; - } - - /** - * @return the size of the file, in bytes - */ - public int getFileSize() { - return fileSize; - } - - /** - * @return the size of the data section, in bytes - */ - public int getDataSize() { - return dataSize; - } - - /** - * @return the offset where the data section begins - */ - public int getDataOffset() { - return dataOffset; - } - - /** - * Set a boolean value indicating whether items in this dex file should be - * written back out "in-place", or whether the normal layout logic should be - * applied. - * - * This should only be used for a dex file that has been read from an existing - * dex file, and no modifications have been made to the dex file. Otherwise, - * there is a good chance that the resulting dex file will be invalid due to - * items that aren't placed correctly - * - * If both SortAllItems and Inplace are true, Inplace takes precedence - * @param value a boolean value indicating whether items in this dex file should be - * written back out in-place. - */ - public void setInplace(boolean value) { - this.inplace = value; - } - - /** - * Get an array of Section objects that are sorted by offset. - * @return an array of Section objects that are sorted by offset. - */ - protected Section[] getOrderedSections() { - int sectionCount = 0; - - for (Section section: sectionsByType) { - if (section != null && section.getItems().size() > 0) { - sectionCount++; - } - } - - Section[] sections = new Section[sectionCount]; - sectionCount = 0; - for (Section section: sectionsByType) { - if (section != null && section.getItems().size() > 0) { - sections[sectionCount++] = section; - } - } - - Arrays.sort(sections, new Comparator
    () { - public int compare(Section a, Section b) { - return a.getOffset() - b.getOffset(); - } - }); - - return sections; - } - - /** - * This method should be called before writing a dex file. It sorts the sections - * as needed or as indicated by getSortAllItems() and getInplace(), - * and then performs a pass through all of the items, finalizing the position (i.e. - * index and/or offset) of each item in the dex file. - * - * This step is needed primarily so that the indexes and offsets of all indexed and - * offsetted items are available when writing references to those items elsewhere. - */ - public void place() { - int offset = HeaderItem.placeAt(0, 0); - - int sectionsPosition = 0; - Section[] sections; - if (this.inplace) { - sections = this.getOrderedSections(); - } else { - sections = new Section[indexedSections.length + offsettedSections.length]; - System.arraycopy(indexedSections, 0, sections, 0, indexedSections.length); - System.arraycopy(offsettedSections, 0, sections, indexedSections.length, offsettedSections.length); - } - - while (sectionsPosition < sections.length && sections[sectionsPosition].ItemType.isIndexedItem()) { - Section section = sections[sectionsPosition]; - if (!this.inplace) { - section.sortSection(); - } - - offset = section.placeAt(offset); - - sectionsPosition++; - } - - dataOffset = offset; - - while (sectionsPosition < sections.length) { - Section section = sections[sectionsPosition]; - if (this.sortAllItems && !this.inplace) { - section.sortSection(); - } - offset = section.placeAt(offset); - - sectionsPosition++; - } - - offset = AlignmentUtils.alignOffset(offset, ItemType.TYPE_MAP_LIST.ItemAlignment); - offset = MapItem.placeAt(offset, 0); - - fileSize = offset; - dataSize = offset - dataOffset; - } - - /** - * Writes the dex file to the give AnnotatedOutput object. If - * out.Annotates() is true, then annotations that document the format - * of the dex file are written. - * - * You must call place() on this dex file, before calling this method - * @param out the AnnotatedOutput object to write the dex file and annotations to - * - * After calling this method, you should call calcSignature() and - * then calcChecksum() on the resulting byte array, to calculate the - * signature and checksum in the header - */ - public void writeTo(AnnotatedOutput out) { - - out.annotate(0, "-----------------------------"); - out.annotate(0, "header item"); - out.annotate(0, "-----------------------------"); - out.annotate(0, " "); - HeaderItem.writeTo(out); - - out.annotate(0, " "); - - int sectionsPosition = 0; - Section[] sections; - if (this.inplace) { - sections = this.getOrderedSections(); - } else { - sections = new Section[indexedSections.length + offsettedSections.length]; - System.arraycopy(indexedSections, 0, sections, 0, indexedSections.length); - System.arraycopy(offsettedSections, 0, sections, indexedSections.length, offsettedSections.length); - } - - while (sectionsPosition < sections.length) { - sections[sectionsPosition].writeTo(out); - sectionsPosition++; - } - - out.alignTo(MapItem.getItemType().ItemAlignment); - - out.annotate(0, " "); - out.annotate(0, "-----------------------------"); - out.annotate(0, "map item"); - out.annotate(0, "-----------------------------"); - out.annotate(0, " "); - MapItem.writeTo(out); - } - - public final HeaderItem HeaderItem = new HeaderItem(this); - public final MapItem MapItem = new MapItem(this); - - /** - * The IndexedSection containing StringIdItem items - */ - public final IndexedSection StringIdsSection = - new IndexedSection(this, ItemType.TYPE_STRING_ID_ITEM); - - /** - * The IndexedSection containing TypeIdItem items - */ - public final IndexedSection TypeIdsSection = - new IndexedSection(this, ItemType.TYPE_TYPE_ID_ITEM); - - /** - * The IndexedSection containing ProtoIdItem items - */ - public final IndexedSection ProtoIdsSection = - new IndexedSection(this, ItemType.TYPE_PROTO_ID_ITEM); - - /** - * The IndexedSection containing FieldIdItem items - */ - public final IndexedSection FieldIdsSection = - new IndexedSection(this, ItemType.TYPE_FIELD_ID_ITEM); - - /** - * The IndexedSection containing MethodIdItem items - */ - public final IndexedSection MethodIdsSection = - new IndexedSection(this, ItemType.TYPE_METHOD_ID_ITEM); - - /** - * The IndexedSection containing ClassDefItem items - */ - public final IndexedSection ClassDefsSection = - new IndexedSection(this, ItemType.TYPE_CLASS_DEF_ITEM) { - - public int placeAt(int offset) { - if (DexFile.this.getInplace()) { - return super.placeAt(offset); - } - - int ret = ClassDefItem.placeClassDefItems(this, offset); - - Collections.sort(this.items); - - this.offset = items.get(0).getOffset(); - return ret; - } - - protected void sortSection() { - // Do nothing. Sorting is handled by ClassDefItem.ClassDefPlacer, during placement - } - }; - - /** - * The OffsettedSection containing TypeListItem items - */ - public final OffsettedSection TypeListsSection = - new OffsettedSection(this, ItemType.TYPE_TYPE_LIST); - - /** - * The OffsettedSection containing AnnotationSetRefList items - */ - public final OffsettedSection AnnotationSetRefListsSection = - new OffsettedSection(this, ItemType.TYPE_ANNOTATION_SET_REF_LIST); - - /** - * The OffsettedSection containing AnnotationSetItem items - */ - public final OffsettedSection AnnotationSetsSection = - new OffsettedSection(this, ItemType.TYPE_ANNOTATION_SET_ITEM); - - /** - * The OffsettedSection containing ClassDataItem items - */ - public final OffsettedSection ClassDataSection = - new OffsettedSection(this, ItemType.TYPE_CLASS_DATA_ITEM); - - /** - * The OffsettedSection containing CodeItem items - */ - public final OffsettedSection CodeItemsSection = - new OffsettedSection(this, ItemType.TYPE_CODE_ITEM); - - /** - * The OffsettedSection containing StringDataItem items - */ - public final OffsettedSection StringDataSection = - new OffsettedSection(this, ItemType.TYPE_STRING_DATA_ITEM); - - /** - * The OffsettedSection containing DebugInfoItem items - */ - public final OffsettedSection DebugInfoItemsSection = - new OffsettedSection(this, ItemType.TYPE_DEBUG_INFO_ITEM); - - /** - * The OffsettedSection containing AnnotationItem items - */ - public final OffsettedSection AnnotationsSection = - new OffsettedSection(this, ItemType.TYPE_ANNOTATION_ITEM); - - /** - * The OffsettedSection containing EncodedArrayItem items - */ - public final OffsettedSection EncodedArraysSection = - new OffsettedSection(this, ItemType.TYPE_ENCODED_ARRAY_ITEM); - - /** - * The OffsettedSection containing AnnotationDirectoryItem items - */ - public final OffsettedSection AnnotationDirectoriesSection = - new OffsettedSection(this, ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM); - - - /** - * Calculates the signature for the dex file in the given byte array, - * and then writes the signature to the appropriate location in the header - * containing in the array - * - * @param bytes non-null; the bytes of the file - */ - public static void calcSignature(byte[] bytes) { - MessageDigest md; - - try { - md = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException(ex); - } - - md.update(bytes, 32, bytes.length - 32); - - try { - int amt = md.digest(bytes, 12, 20); - if (amt != 20) { - throw new RuntimeException("unexpected digest write: " + amt + - " bytes"); - } - } catch (DigestException ex) { - throw new RuntimeException(ex); - } - } - - /** - * Calculates the checksum for the .dex file in the - * given array, and modify the array to contain it. - * - * @param bytes non-null; the bytes of the file - */ - public static void calcChecksum(byte[] bytes) { - Adler32 a32 = new Adler32(); - - a32.update(bytes, 12, bytes.length - 12); - - int sum = (int) a32.getValue(); - - bytes[8] = (byte) sum; - bytes[9] = (byte) (sum >> 8); - bytes[10] = (byte) (sum >> 16); - bytes[11] = (byte) (sum >> 24); - } - - public static class NoClassesDexException extends ExceptionWithContext { - public NoClassesDexException(String message) { - super(message); - } - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java deleted file mode 100644 index 6eb917cb..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedArrayItem.java +++ /dev/null @@ -1,135 +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.Util.AnnotatedOutput; -import org.jf.dexlib.Util.Input; - -public class EncodedArrayItem extends Item { - private int hashCode = 0; - - private ArrayEncodedSubValue encodedArray; - - /** - * Creates a new uninitialized EncodedArrayItem - * @param dexFile The DexFile that this item belongs to - */ - protected EncodedArrayItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new EncodedArrayItem with the given values - * @param dexFile The DexFile that this item belongs to - * @param encodedArray The encoded array value - */ - private EncodedArrayItem(DexFile dexFile, ArrayEncodedSubValue encodedArray) { - super(dexFile); - this.encodedArray = encodedArray; - } - - /** - * Returns an EncodedArrayItem for the given values, and that has been interned into the given - * DexFile - * @param dexFile The DexFile that this item belongs to - * @param encodedArray The encoded array value - * @return an EncodedArrayItem for the given values, and that has been interned into the given - */ - public static EncodedArrayItem internEncodedArrayItem(DexFile dexFile, ArrayEncodedSubValue encodedArray) { - EncodedArrayItem encodedArrayItem = new EncodedArrayItem(dexFile, encodedArray); - return dexFile.EncodedArraysSection.intern(encodedArrayItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - encodedArray = new ArrayEncodedSubValue(dexFile, in); - } - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - return encodedArray.placeValue(offset); - } - - /** {@inheritDoc} */ - protected void writeItem(AnnotatedOutput out) { - encodedArray.writeValue(out); - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_ENCODED_ARRAY_ITEM; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "encoded_array @0x" + Integer.toHexString(getOffset()); - } - - /** {@inheritDoc} */ - public int compareTo(EncodedArrayItem encodedArrayItem) { - return encodedArray.compareTo(encodedArrayItem.encodedArray); - } - - /** - * @return The encoded array value - */ - public ArrayEncodedSubValue getEncodedArray() { - return encodedArray; - } - - /** - * calculate and cache the hashcode - */ - private void calcHashCode() { - hashCode = encodedArray.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; - } - - EncodedArrayItem other = (EncodedArrayItem)o; - return (encodedArray.compareTo(other.encodedArray) == 0); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java deleted file mode 100644 index acd59961..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/AnnotationEncodedSubValue.java +++ /dev/null @@ -1,169 +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.EncodedValue; - -import org.jf.dexlib.DexFile; -import org.jf.dexlib.StringIdItem; -import org.jf.dexlib.TypeIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.Input; -import org.jf.dexlib.Util.Leb128Utils; - -/** - * An AnnotationEncodedSubValue is identical to an AnnotationEncodedValue, except that it - * doesn't have the initial valueType/valueArg byte. This is used in the AnnotationItem object - */ -public class AnnotationEncodedSubValue extends EncodedValue { - private int hashCode = 0; - - public final TypeIdItem annotationType; - public final StringIdItem[] names; - public final EncodedValue[] values; - - /** - * Constructs a new AnnotationEncodedSubValue by reading the value from the given Input - * object. - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - */ - public AnnotationEncodedSubValue(DexFile dexFile, Input in) { - annotationType = dexFile.TypeIdsSection.getItemByIndex(in.readUnsignedLeb128()); - names = new StringIdItem[in.readUnsignedLeb128()]; - values = new EncodedValue[names.length]; - - for (int i=0; iAnnotationEncodedValue with the given values. names and values must be the same - * length, and must be sorted according to the name - * @param annotationType The type of the annotation - * @param names An array of the names of the elements of the annotation - * @param values An array of the values of the elements on the annotation - */ - public AnnotationEncodedSubValue(TypeIdItem annotationType, StringIdItem[] names, EncodedValue[] values) { - this.annotationType = annotationType; - if (names.length != values.length) { - throw new RuntimeException("The names and values parameters must be the same length"); - } - this.names = names; - this.values = values; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - out.annotate("annotation_type: " + annotationType.getTypeDescriptor()); - out.writeUnsignedLeb128(annotationType.getIndex()); - out.annotate("element_count: 0x" + Integer.toHexString(names.length) + " (" + names.length + ")"); - out.writeUnsignedLeb128(names.length); - - for (int i=0; iAnnotationEncodedValue by reading the value from the given Input - * object. The Input's cursor should be set to the 2nd byte of the encoded value - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - */ - protected AnnotationEncodedValue(DexFile dexFile, Input in) { - super(dexFile, in); - } - - /** - * Constructs a new AnnotationEncodedValue with the given values. names and values must be the same - * length, and must be sorted according to the name - * @param annotationType The type of the annotation - * @param names An array of the names of the elements of the annotation - * @param values An array of the values of the elements on the annotation - */ - public AnnotationEncodedValue(TypeIdItem annotationType, StringIdItem[] names, EncodedValue[] values) { - super(annotationType, names, values); - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate("value_type=" + ValueType.VALUE_ANNOTATION.name() + ",value_arg=0"); - } - out.writeByte(ValueType.VALUE_ANNOTATION.value); - super.writeValue(out); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return super.placeValue(offset + 1); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java deleted file mode 100644 index f79db736..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ArrayEncodedSubValue.java +++ /dev/null @@ -1,141 +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.EncodedValue; - -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.Input; -import org.jf.dexlib.Util.Leb128Utils; - -/** - * An ArrayEncodedSubValue is identical to an ArrayEncodedValue, except that it - * doesn't have the initial valueType/valueArg byte. This is used in the EncodedArrayItem object - */ -public class ArrayEncodedSubValue extends EncodedValue { - private int hashCode = 0; - - public final EncodedValue[] values; - - /** - * Constructs a new ArrayEncodedSubValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - */ - public ArrayEncodedSubValue(DexFile dexFile, Input in) { - values = new EncodedValue[in.readUnsignedLeb128()]; - - for (int i=0; iArrayEncodedSubValue with the given values - * @param values The array values - */ - public ArrayEncodedSubValue(EncodedValue[] values) { - this.values = values; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - if (out.annotates()) - { - out.annotate("array_size: 0x" + Integer.toHexString(values.length) + " (" + values.length + ")"); - out.writeUnsignedLeb128(values.length); - int index = 0; - for (EncodedValue encodedValue: values) { - out.annotate(0, "[" + index++ + "] array_element"); - out.indent(); - encodedValue.writeValue(out); - out.deindent(); - } - } else { - out.writeUnsignedLeb128(values.length); - for (EncodedValue encodedValue: values) { - encodedValue.writeValue(out); - } - } - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - offset = offset + Leb128Utils.unsignedLeb128Size(values.length); - for (EncodedValue encodedValue: values) { - offset = encodedValue.placeValue(offset); - } - - return offset; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - ArrayEncodedSubValue other = (ArrayEncodedSubValue)o; - - int comp = values.length - other.values.length; - if (comp != 0) { - return comp; - } - - for (int i=0; iArrayEncodedValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - */ - protected ArrayEncodedValue(DexFile dexFile, Input in) { - super(dexFile, in); - } - - /** - * Constructs a new ArrayEncodedValue with the given values - * @param values The array values - */ - public ArrayEncodedValue(EncodedValue[] values) { - super(values); - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate("value_type=" + ValueType.VALUE_ARRAY.name() + ",value_arg=0"); - } - out.writeByte(ValueType.VALUE_ARRAY.value); - super.writeValue(out); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return super.placeValue(offset + 1); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java deleted file mode 100644 index 507e58fe..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/BooleanEncodedValue.java +++ /dev/null @@ -1,109 +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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; - -public class BooleanEncodedValue extends EncodedValue { - /** - * The dupliton values - */ - public static final BooleanEncodedValue TrueValue = new BooleanEncodedValue(true); - public static final BooleanEncodedValue FalseValue = new BooleanEncodedValue(false); - - public final boolean value; - - /** - * Constructs a new BooleanEncodedValue with the given value - * @param value The value - */ - private BooleanEncodedValue(boolean value) { - this.value = value; - } - - /** - * Gets the BooleanEncodedValue for the given valueArg value. The high 3 bits of the first byte should - * be passed as the valueArg parameter - * @param valueArg The high 3 bits of the first byte of this encoded value - * @return the BooleanEncodedValue for the given valueArg value - */ - protected static BooleanEncodedValue getBooleanEncodedValue(byte valueArg) { - if (valueArg == 0) { - return FalseValue; - } else if (valueArg == 1) { - return TrueValue; - } - throw new RuntimeException("valueArg must be either 0 or 1"); - } - - /** - * Gets the BooleanEncodedValue for the given boolean value - * @param value the boolean value - * @return the BooleanEncodedValue for the given boolean value - */ - public static BooleanEncodedValue getBooleanEncodedValue(boolean value) { - if (value) { - return TrueValue; - } - return FalseValue; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate("value_type=" + ValueType.VALUE_BOOLEAN.name() + ",value=" + Boolean.toString(value)); - } - out.writeByte(ValueType.VALUE_BOOLEAN.value | ((value?1:0) << 5)); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - BooleanEncodedValue other = (BooleanEncodedValue)o; - if (value == other.value) - return 0; - if (value) - return 1; - return -1; - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_BOOLEAN; - } - - @Override - public int hashCode() { - return value?1:0; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java deleted file mode 100644 index 683d547e..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ByteEncodedValue.java +++ /dev/null @@ -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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class ByteEncodedValue extends EncodedValue { - public final byte value; - - /** - * Constructs a new ByteEncodedValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value - * @param in The Input object to read from - */ - protected ByteEncodedValue(Input in) { - value = (byte)EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(1)); - } - - /** - * Constructs a new ByteEncodedValue with the given value - * @param value The value - */ - public ByteEncodedValue(byte value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_BYTE.name() + ",value_arg=0"); - out.annotate(1, "value: 0x" + Integer.toHexString(value) + " (" + value + ")"); - } - out.writeByte(ValueType.VALUE_BYTE.value); - out.writeByte(value); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + 2; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - ByteEncodedValue other = (ByteEncodedValue)o; - - return (valueother.value?1:0)); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_BYTE; - } - - @Override - public int hashCode() { - return value; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java deleted file mode 100644 index 25646612..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/CharEncodedValue.java +++ /dev/null @@ -1,93 +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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class CharEncodedValue extends EncodedValue { - public final char value; - - /** - * Constructs a new CharEncodedValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of - * the first byte should be passed as the valueArg parameter - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected CharEncodedValue(Input in, byte valueArg) { - value = (char)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); - } - - /** - * Constructs a new CharEncodedValue with the given value - * @param value The value - */ - public CharEncodedValue(char value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_CHAR.name() + ",value_arg=" + (bytes.length - 1)); - char[] c = Character.toChars(value); - assert c.length > 0; - out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " '" + c[0] + "'"); - } - - out.writeByte(ValueType.VALUE_CHAR.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - CharEncodedValue other = (CharEncodedValue)o; - - return (valueother.value?1:0)); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_CHAR; - } - - @Override - public int hashCode() { - return value; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java deleted file mode 100644 index 6b8bcc37..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/DoubleEncodedValue.java +++ /dev/null @@ -1,94 +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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class DoubleEncodedValue extends EncodedValue { - public final double value; - - /** - * Constructs a new DoubleEncodedValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of - * the first byte should be passed as the valueArg parameter - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected DoubleEncodedValue(Input in, byte valueArg) { - long longValue = EncodedValueUtils.decodeRightZeroExtendedValue(in.readBytes(valueArg + 1)); - value = Double.longBitsToDouble(longValue); - } - - /** - * Constructs a new DoubleEncodedValue with the given value - * @param value The value - */ - public DoubleEncodedValue(double value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(Double.doubleToRawLongBits(value)); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_DOUBLE.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: " + value); - } - - out.writeByte(ValueType.VALUE_DOUBLE.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + 1 + EncodedValueUtils.getRequiredBytesForRightZeroExtendedValue( - Double.doubleToRawLongBits(value)); - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - DoubleEncodedValue other = (DoubleEncodedValue)o; - - return Double.compare(value, other.value); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_DOUBLE; - } - - @Override - public int hashCode() { - return (int)Double.doubleToRawLongBits(value); - } -} - diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java deleted file mode 100644 index 4c00faf2..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EncodedValue.java +++ /dev/null @@ -1,125 +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.EncodedValue; - -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.Input; - -public abstract class EncodedValue implements Comparable { - /** - * Writes this EncodedValue to the given AnnotatedOutput object - * @param out the AnnotatedOutput object to write to - */ - public abstract void writeValue(AnnotatedOutput out); - - /** - * Calculates the size of this encoded value and returns offset + size; - * @param offset The offset to place this encoded value - * @return the offset immediately after this encoded value - */ - public abstract int placeValue(int offset); - - - public static EncodedValue readEncodedValue(DexFile dexFile, Input in) { - Byte b = in.readByte(); - ValueType valueType = ValueType.fromByte((byte)(b & 0x1f)); - byte valueArg = (byte)((b & 0xFF) >> 5); - - switch (valueType) { - case VALUE_BYTE: - return new ByteEncodedValue(in); - case VALUE_SHORT: - return new ShortEncodedValue(in, valueArg); - case VALUE_CHAR: - return new CharEncodedValue(in, valueArg); - case VALUE_INT: - return new IntEncodedValue(in, valueArg); - case VALUE_LONG: - return new LongEncodedValue(in, valueArg); - case VALUE_FLOAT: - return new FloatEncodedValue(in, valueArg); - case VALUE_DOUBLE: - return new DoubleEncodedValue(in, valueArg); - case VALUE_STRING: - return new StringEncodedValue(dexFile, in, valueArg); - case VALUE_TYPE: - return new TypeEncodedValue(dexFile, in, valueArg); - case VALUE_FIELD: - return new FieldEncodedValue(dexFile, in, valueArg); - case VALUE_METHOD: - return new MethodEncodedValue(dexFile, in, valueArg); - case VALUE_ENUM: - return new EnumEncodedValue(dexFile, in, valueArg); - case VALUE_ARRAY: - return new ArrayEncodedValue(dexFile, in); - case VALUE_ANNOTATION: - return new AnnotationEncodedValue(dexFile, in); - case VALUE_NULL: - return NullEncodedValue.NullValue; - case VALUE_BOOLEAN: - return BooleanEncodedValue.getBooleanEncodedValue(valueArg); - } - return null; - } - - /** {@inheritDoc} */ - public int compareTo(EncodedValue o) { - int comp = getValueType().compareTo(o.getValueType()); - if (comp == 0) { - comp = compareValue(o); - } - return comp; - } - - /** - * Compare the value of this EncodedValue against the value of the given , which - * is guaranteed to be of the same type as this EncodedValue - * @param o The EncodedValue to compare against - * @return A standard comparison integer value - */ - protected abstract int compareValue(EncodedValue o); - - /** - * @return the ValueType representing the type of this EncodedValue - */ - public abstract ValueType getValueType(); - - @Override - public boolean equals(Object o) { - if (this==o) { - return true; - } - if (o==null || !(o instanceof EncodedValue)) { - return false; - } - - return this.compareTo((EncodedValue)o) == 0; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java deleted file mode 100644 index 7cd1f45a..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/EnumEncodedValue.java +++ /dev/null @@ -1,95 +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.EncodedValue; - -import org.jf.dexlib.DexFile; -import org.jf.dexlib.FieldIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class EnumEncodedValue extends EncodedValue { - public final FieldIdItem value; - - /** - * Constructs a new EnumEncodedValue by reading the field index from the given Input - * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits - * of the first byte should be passed as the valueArg parameter - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected EnumEncodedValue(DexFile dexFile, Input in, byte valueArg) { - int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); - value = dexFile.FieldIdsSection.getItemByIndex(index); - } - - /** - * Constructs a new EnumEncodedValue with the given FieldIdItem value - * @param value The FieldIdItem value - */ - public EnumEncodedValue(FieldIdItem value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_ENUM.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: " + value.getFieldString()); - } - - out.writeByte(ValueType.VALUE_ENUM.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - EnumEncodedValue other = (EnumEncodedValue)o; - - return value.compareTo(other.value); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_ENUM; - } - - @Override - public int hashCode() { - return value.hashCode(); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java deleted file mode 100644 index 6aafc621..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FieldEncodedValue.java +++ /dev/null @@ -1,95 +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.EncodedValue; - -import org.jf.dexlib.DexFile; -import org.jf.dexlib.FieldIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class FieldEncodedValue extends EncodedValue { - public final FieldIdItem value; - - /** - * Constructs a new FieldEncodedValue by reading the field index from the given Input - * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits - * of the first byte should be passed as the valueArg parameter - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected FieldEncodedValue(DexFile dexFile, Input in, byte valueArg) { - int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); - value = dexFile.FieldIdsSection.getItemByIndex(index); - } - - /** - * Constructs a new FieldEncodedValue with the given FieldIdItem value - * @param value The FieldIdItem value - */ - public FieldEncodedValue(FieldIdItem value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_FIELD.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: " + value.getFieldString()); - } - - out.writeByte(ValueType.VALUE_FIELD.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - FieldEncodedValue other = (FieldEncodedValue)o; - - return value.compareTo(other.value); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_FIELD; - } - - @Override - public int hashCode() { - return value.hashCode(); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java deleted file mode 100644 index af514f4f..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/FloatEncodedValue.java +++ /dev/null @@ -1,93 +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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class FloatEncodedValue extends EncodedValue { - public final float value; - - /** - * Constructs a new FloatEncodedValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of - * the first byte should be passed as the valueArg parameter - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected FloatEncodedValue(Input in, byte valueArg) { - long longValue = EncodedValueUtils.decodeRightZeroExtendedValue(in.readBytes(valueArg + 1)); - value = Float.intBitsToFloat((int)((longValue >> 32) & 0xFFFFFFFFL)); - } - - /** - * Constructs a new FloatEncodedValue with the given value - * @param value The value - */ - public FloatEncodedValue(float value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(((long)Float.floatToRawIntBits(value)) << 32); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_FLOAT.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: " + value); - } - - out.writeByte(ValueType.VALUE_FLOAT.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + 1 + EncodedValueUtils.getRequiredBytesForRightZeroExtendedValue( - ((long)Float.floatToRawIntBits(value)) << 32); - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - FloatEncodedValue other = (FloatEncodedValue)o; - - return Float.compare(value, other.value); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_FLOAT; - } - - @Override - public int hashCode() { - return Float.floatToRawIntBits(value); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java deleted file mode 100644 index 7da5b021..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/IntEncodedValue.java +++ /dev/null @@ -1,91 +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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class IntEncodedValue extends EncodedValue { - public final int value; - - /** - * Constructs a new IntEncodedValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of - * the first byte should be passed as the valueArg parameter - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected IntEncodedValue(Input in, byte valueArg) { - value = (int)EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1)); - } - - /** - * Constructs a new IntEncodedValue with the given value - * @param value The value - */ - public IntEncodedValue(int value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_INT.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " (" + value + ")"); - } - - out.writeByte(ValueType.VALUE_INT.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - IntEncodedValue other = (IntEncodedValue)o; - - return (valueother.value?1:0)); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_INT; - } - - @Override - public int hashCode() { - return value; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java deleted file mode 100644 index 7db6ed13..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/LongEncodedValue.java +++ /dev/null @@ -1,91 +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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class LongEncodedValue extends EncodedValue { - public final long value; - - /** - * Constructs a new LongEncodedValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of - * the first byte should be passed as the valueArg parameter - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected LongEncodedValue(Input in, byte valueArg) { - value = EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1)); - } - - /** - * Constructs a new LongEncodedValue with the given value - * @param value The value - */ - public LongEncodedValue(long value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_LONG.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: 0x" + Long.toHexString(value) + " (" + value + ")"); - } - - out.writeByte(ValueType.VALUE_LONG.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - LongEncodedValue other = (LongEncodedValue)o; - - return (valueother.value?1:0)); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_LONG; - } - - @Override - public int hashCode() { - return (int)value; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java deleted file mode 100644 index e23450a2..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/MethodEncodedValue.java +++ /dev/null @@ -1,95 +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.EncodedValue; - -import org.jf.dexlib.DexFile; -import org.jf.dexlib.MethodIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class MethodEncodedValue extends EncodedValue { - public final MethodIdItem value; - - /** - * Constructs a new MethodEncodedValue by reading the method index from the given Input - * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits - * of the first byte should be passed as the valueArg parameter - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected MethodEncodedValue(DexFile dexFile, Input in, byte valueArg) { - int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); - value = dexFile.MethodIdsSection.getItemByIndex(index); - } - - /** - * Constructs a new MethodEncodedValue with the given MethodIdItem value - * @param value The MethodIdItem value - */ - public MethodEncodedValue(MethodIdItem value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_METHOD.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: " + value.getMethodString()); - } - - out.writeByte(ValueType.VALUE_METHOD.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - MethodEncodedValue other = (MethodEncodedValue)o; - - return value.compareTo(other.value); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_METHOD; - } - - @Override - public int hashCode() { - return value.hashCode(); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java deleted file mode 100644 index 334b82a3..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/NullEncodedValue.java +++ /dev/null @@ -1,72 +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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; - -public class NullEncodedValue extends EncodedValue { - /** - * The singleton value - */ - public static final NullEncodedValue NullValue = new NullEncodedValue(); - - /** - * Constructs a new NullEncodedValue - */ - private NullEncodedValue() { - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate("value_type=" + ValueType.VALUE_NULL.name() + ",value_arg=0"); - } - out.writeByte(ValueType.VALUE_NULL.value); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - return 0; - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_NULL; - } - - @Override - public int hashCode() { - return 1; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java deleted file mode 100644 index 66d80e17..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ShortEncodedValue.java +++ /dev/null @@ -1,91 +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.EncodedValue; - -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class ShortEncodedValue extends EncodedValue { - public final short value; - - /** - * Constructs a new ShortEncodedValue by reading the value from the given Input object. - * The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits of - * the first byte should be passed as the valueArg parameter - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected ShortEncodedValue(Input in, byte valueArg) { - value = (short) EncodedValueUtils.decodeSignedIntegralValue(in.readBytes(valueArg+1)); - } - - /** - * Constructs a new ShortEncodedValue with the given value - * @param value The value - */ - public ShortEncodedValue(short value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_SHORT.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: 0x" + Integer.toHexString(value) + " (" + value + ")"); - } - - out.writeByte(ValueType.VALUE_SHORT.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForSignedIntegralValue(value) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - ShortEncodedValue other = (ShortEncodedValue)o; - - return (valueother.value?1:0)); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_SHORT; - } - - @Override - public int hashCode() { - return value; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java deleted file mode 100644 index 8b323945..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/StringEncodedValue.java +++ /dev/null @@ -1,96 +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.EncodedValue; - -import org.jf.dexlib.DexFile; -import org.jf.dexlib.StringIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; -import org.jf.dexlib.Util.Utf8Utils; - -public class StringEncodedValue extends EncodedValue { - public final StringIdItem value; - - /** - * Constructs a new StringEncodedValue by reading the string index from the given Input - * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits - * of the first byte should be passed as the valueArg parameter - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected StringEncodedValue(DexFile dexFile, Input in, byte valueArg) { - int index = (int)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); - value = dexFile.StringIdsSection.getItemByIndex(index); - } - - /** - * Constructs a new StringEncodedValue with the given StringIdItem value - * @param value The StringIdItem value - */ - public StringEncodedValue(StringIdItem value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_STRING.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: \"" + Utf8Utils.escapeString(value.getStringValue()) + "\""); - } - - out.writeByte(ValueType.VALUE_STRING.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - StringEncodedValue other = (StringEncodedValue)o; - - return value.compareTo(other.value); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_STRING; - } - - @Override - public int hashCode() { - return value.hashCode(); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java deleted file mode 100644 index 335aab65..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/TypeEncodedValue.java +++ /dev/null @@ -1,95 +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.EncodedValue; - -import org.jf.dexlib.DexFile; -import org.jf.dexlib.TypeIdItem; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.EncodedValueUtils; -import org.jf.dexlib.Util.Input; - -public class TypeEncodedValue extends EncodedValue { - public final TypeIdItem value; - - /** - * Constructs a new TypeEncodedValue by reading the type index from the given Input - * object. The Input's cursor should be set to the 2nd byte of the encoded value, and the high 3 bits - * of the first byte should be passed as the valueArg parameter - * @param dexFile The DexFile that is being read in - * @param in The Input object to read from - * @param valueArg The high 3 bits of the first byte of this encoded value - */ - protected TypeEncodedValue(DexFile dexFile, Input in, byte valueArg) { - int index = (int) EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(valueArg+1)); - value = dexFile.TypeIdsSection.getItemByIndex(index); - } - - /** - * Constructs a new TypeEncodedValue with the given TypeIdItem value - * @param value The TypeIdItem value - */ - public TypeEncodedValue(TypeIdItem value) { - this.value = value; - } - - /** {@inheritDoc} */ - public void writeValue(AnnotatedOutput out) { - byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value.getIndex()); - - if (out.annotates()) { - out.annotate(1, "value_type=" + ValueType.VALUE_TYPE.name() + ",value_arg=" + (bytes.length - 1)); - out.annotate(bytes.length, "value: " + value.getTypeDescriptor()); - } - - out.writeByte(ValueType.VALUE_TYPE.value | ((bytes.length - 1) << 5)); - out.write(bytes); - } - - /** {@inheritDoc} */ - public int placeValue(int offset) { - return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(value.getIndex()) + 1; - } - - /** {@inheritDoc} */ - protected int compareValue(EncodedValue o) { - TypeEncodedValue other = (TypeEncodedValue)o; - - return value.compareTo(other.value); - } - - /** {@inheritDoc} */ - public ValueType getValueType() { - return ValueType.VALUE_TYPE; - } - - @Override - public int hashCode() { - return value.hashCode(); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java deleted file mode 100644 index d33b0ac2..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/EncodedValue/ValueType.java +++ /dev/null @@ -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.EncodedValue; - -import org.jf.dexlib.Util.SparseArray; - -public enum ValueType { - - VALUE_BYTE((byte) 0x00), - VALUE_SHORT((byte) 0x02), - VALUE_CHAR((byte) 0x03), - VALUE_INT((byte) 0x04), - VALUE_LONG((byte) 0x06), - VALUE_FLOAT((byte) 0x10), - VALUE_DOUBLE((byte) 0x11), - VALUE_STRING((byte) 0x17), - VALUE_TYPE((byte) 0x18), - VALUE_FIELD((byte) 0x19), - VALUE_METHOD((byte) 0x1a), - VALUE_ENUM((byte) 0x1b), - VALUE_ARRAY((byte) 0x1c), - VALUE_ANNOTATION((byte) 0x1d), - VALUE_NULL((byte) 0x1e), - VALUE_BOOLEAN((byte) 0x1f); - - /** - * A map to facilitate looking up a ValueType by byte value - */ - private final static SparseArray valueTypeIntegerMap; - - static { - /** build the valueTypeIntegerMap object */ - valueTypeIntegerMap = new SparseArray(16); - - for (ValueType valueType : ValueType.values()) { - valueTypeIntegerMap.put(valueType.value, valueType); - } - } - - /** - * The byte value for this ValueType - */ - public final byte value; - - private ValueType(byte value) { - this.value = value; - } - - /** - * Converts a byte value to the corresponding ValueType enum value, - * or null if the value isn't a valid ValueType value - * - * @param valueType the byte value to convert to a ValueType - * @return the ValueType enum value corresponding to valueType, or null - * if not a valid ValueType value - */ - public static ValueType fromByte(byte valueType) { - return valueTypeIntegerMap.get(valueType); - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java deleted file mode 100644 index c9fe05f0..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/FieldIdItem.java +++ /dev/null @@ -1,263 +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; - -public class FieldIdItem extends Item implements Convertible { - private int hashCode = 0; - - private TypeIdItem classType; - private TypeIdItem fieldType; - private StringIdItem fieldName; - - /** - * Creates a new uninitialized FieldIdItem - * @param dexFile The DexFile that this item belongs to - */ - protected FieldIdItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new FieldIdItem for the given class, type and name - * @param dexFile The DexFile that this item belongs to - * @param classType the class that the field is a member of - * @param fieldType the type of the field - * @param fieldName the name of the field - */ - private FieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, StringIdItem fieldName) { - this(dexFile); - - assert classType.dexFile == dexFile; - assert fieldType.dexFile == dexFile; - assert fieldName.dexFile == dexFile; - - this.classType = classType; - this.fieldType = fieldType; - this.fieldName = fieldName; - } - - /** - * Returns a FieldIdItem for the given values, and that has been interned into - * the given DexFile - * @param dexFile The DexFile that this item belongs to - * @param classType the class that the field is a member of - * @param fieldType the type of the field - * @param fieldName the name of the field - * @return a FieldIdItem for the given values, and that has been interned into - * the given DexFile - */ - public static FieldIdItem internFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, - StringIdItem fieldName) { - FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName); - return dexFile.FieldIdsSection.intern(fieldIdItem); - } - - /** - * Looks up a FieldIdItem from the given DexFile for the given - * values - * @param dexFile The DexFile that this item belongs to - * @param classType the class that the field is a member of - * @param fieldType the type of the field - * @param fieldName the name of the field - * @return a FieldIdItem from the given DexFile for the given - * values, or null if it doesn't exist - */ - public static FieldIdItem lookupFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, - StringIdItem fieldName) { - FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName); - return dexFile.FieldIdsSection.getInternedItem(fieldIdItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort()); - fieldType = dexFile.TypeIdsSection.getItemByIndex(in.readShort()); - fieldName = dexFile.StringIdsSection.getItemByIndex(in.readInt()); - } - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - return offset + 8; - } - - /** {@inheritDoc} */ - protected void writeItem(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate(2, "class_type: " + classType.getTypeDescriptor()); - out.annotate(2, "field_type: " + fieldType.getTypeDescriptor()); - out.annotate(4, "field_name: " + fieldName.getStringValue()); - } - - int classIndex = classType.getIndex(); - if (classIndex > 0xffff) { - throw new RuntimeException(String.format("Error writing field_id_item for %s. The type index of " + - "defining class %s is too large", getFieldString(), classType.getTypeDescriptor())); - } - out.writeShort(classIndex); - - int typeIndex = fieldType.getIndex(); - if (typeIndex > 0xffff) { - throw new RuntimeException(String.format("Error writing field_id_item for %s. The type index of field " + - "type %s is too large", getFieldString(), fieldType.getTypeDescriptor())); - } - out.writeShort(typeIndex); - - out.writeInt(fieldName.getIndex()); - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_FIELD_ID_ITEM; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return getFieldString(); - } - - /** {@inheritDoc} */ - public int compareTo(FieldIdItem o) { - int result = classType.compareTo(o.classType); - if (result != 0) { - return result; - } - - result = fieldName.compareTo(o.fieldName); - if (result != 0) { - return result; - } - - return fieldType.compareTo(o.fieldType); - } - - /** - * @return the class that this field is a member of - */ - public TypeIdItem getContainingClass() { - return classType; - } - - /** - * @return the type of this field - */ - public TypeIdItem getFieldType() { - return fieldType; - } - - /** - * @return the field name - */ - public StringIdItem getFieldName() { - return fieldName; - } - - String cachedFieldString = null; - /** - * @return a string formatted like LclassName;->fieldName:fieldType - */ - public String getFieldString() { - if (cachedFieldString == null) { - String typeDescriptor = classType.getTypeDescriptor(); - String fieldName = this.fieldName.getStringValue(); - String fieldType = this.fieldType.getTypeDescriptor(); - - StringBuffer sb = new StringBuffer(typeDescriptor.length() + fieldName.length() + fieldType.length() + 3); - sb.append(typeDescriptor); - sb.append("->"); - sb.append(fieldName); - sb.append(":"); - sb.append(fieldType); - cachedFieldString = sb.toString(); - } - return cachedFieldString; - } - - String cachedShortFieldString = null; - /** - * @return a "short" string containing just the field name and type, formatted like fieldName:fieldType - */ - public String getShortFieldString() { - if (cachedShortFieldString == null) { - String fieldName = this.fieldName.getStringValue(); - String fieldType = this.fieldType.getTypeDescriptor(); - - StringBuffer sb = new StringBuffer(fieldName.length() + fieldType.length() + 1); - sb.append(fieldName); - sb.append(":"); - sb.append(fieldType); - cachedShortFieldString = sb.toString(); - } - return cachedShortFieldString; - } - - - /** - * calculate and cache the hashcode - */ - private void calcHashCode() { - hashCode = classType.hashCode(); - hashCode = 31 * hashCode + fieldType.hashCode(); - hashCode = 31 * hashCode + fieldName.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; - } - - //This assumes that the referenced items have been interned in both objects. - //This is a valid assumption because all outside code must use the static - //"getInterned..." style methods to make new items, and any item created - //internally is guaranteed to be interned - FieldIdItem other = (FieldIdItem)o; - return (classType == other.classType && - fieldType == other.fieldType && - fieldName == other.fieldName); - } - - public FieldIdItem convert() { - return this; - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java deleted file mode 100644 index e3f9a0f3..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java +++ /dev/null @@ -1,301 +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.Input; -import org.jf.dexlib.Util.Utf8Utils; - -public class HeaderItem extends Item { - /** - * the file format magic number, represented as the - * low-order bytes of a string - */ - public static final byte[][] MAGIC_VALUES = new byte[][] { - new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00}, //"dex\n035" + '\0'; - new byte[] {0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x36, 0x00}}; //"dex\n036" + '\0'; - - - /** size of this section, in bytes */ - private static final int HEADER_SIZE = 0x70; - - /** the endianness constants */ - private static final int LITTLE_ENDIAN = 0x12345678; - private static final int BIG_ENDIAN = 0x78563412; - - /* Which magic value to use when writing out the header item */ - private int magic_index = 0; - - private boolean checksumSignatureSet = false; - private int checksum; - private byte[] signature; - - /** - * Create a new uninitialized HeaderItem - * @param dexFile The DexFile containing this HeaderItem - */ - protected HeaderItem(final DexFile dexFile) { - super(dexFile); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - byte[] readMagic = in.readBytes(8); - - boolean success = false; - for (int i=0; i extends Section { - - /** - * Create a new indexed section - * @param dexFile The DexFile that this section belongs to - * @param itemType The itemType that this section will hold - */ - public IndexedSection(DexFile dexFile, ItemType itemType) { - super(dexFile, itemType); - } - - /** {@inheritDoc} */ - protected void readItems(Input in, ReadContext readContext) { - for (int i = 0; i < items.size(); i++) { - T item = (T)ItemFactory.makeItem(ItemType, DexFile); - items.set(i, item); - item.readFrom(in, i, readContext); - } - } - - /** - * Gets the item at the specified index in this section, or null if the index is -1 - * @param index the index of the item to get - * @return the item at the specified index in this section, or null if the index is -1 - */ - public T getOptionalItemByIndex(int index) { - if (index == -1) { - return null; - } - - return getItemByIndex(index); - } - - /** - * Gets the item at the specified index in this section - * @param index the index of the item to get - * @return the item at the specified index in this section - */ - public T getItemByIndex(int index) { - try { - //if index is out of bounds, just let it throw an exception - return items.get(index); - } catch (Exception ex) { - throw ExceptionWithContext.withContext(ex, "Error occured while retrieving the " + this.ItemType.TypeName + - " item at index " + index); - } - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Item.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Item.java deleted file mode 100644 index 98c23389..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Item.java +++ /dev/null @@ -1,224 +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.AlignmentUtils; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.ExceptionWithContext; -import org.jf.dexlib.Util.Input; - -public abstract class Item implements Comparable { - /** - * The offset of this item in the dex file, or -1 if not known - */ - protected int offset = -1; - - /** - * The index of this item in the containing section, or -1 if not known - */ - protected int index = -1; - - /** - * The DexFile that this item is associatedr with - */ - protected final DexFile dexFile; - - /** - * The constructor that is used when reading in a DexFile - * @param dexFile the DexFile that this item is associated with - */ - protected Item(DexFile dexFile) { - assert dexFile != null; - - this.dexFile = dexFile; - } - - /** - * Read in the item from the given input stream, and initialize the index - * @param in the Input object to read from - * @param index the index within the containing section of the item being read in - * @param readContext a ReadContext object to hold information that is - * only needed while reading in a file - */ - protected void readFrom(Input in, int index, ReadContext readContext) { - try { - assert AlignmentUtils.isAligned(in.getCursor(), getItemType().ItemAlignment); - - this.offset = in.getCursor(); - this.index = index; - - this.readItem(in, readContext); - } catch (Exception ex) { - throw addExceptionContext(ex); - } - } - - /** - * Place the item at the given offset and index, and return the offset of the byte following this item - * @param offset The offset to place the item at - * @param index The index of the item within the containing section - * @return The offset of the byte following this item - */ - protected int placeAt(int offset, int index) { - try { - assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment); - assert !dexFile.getInplace() || (offset == this.offset && this.index == index); - - this.offset = offset; - this.index = index; - return this.placeItem(offset); - } catch (Exception ex) { - throw addExceptionContext(ex); - } - } - - /** - * Write and annotate this item to the output stream - * @param out The output stream to write and annotate to - */ - protected void writeTo(AnnotatedOutput out) { - try { - assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment); - //ensure that it is being written to the same offset where it was previously placed - assert out.getCursor() == offset; - - if (out.annotates()) { - out.annotate(0, "[" + index + "] " + this.getItemType().TypeName); - } - - out.indent(); - writeItem(out); - out.deindent(); - } catch (Exception ex) { - throw addExceptionContext(ex); - } - } - - /** - * Returns a human readable form of this item - * @return a human readable form of this item - */ - public String toString() { - return getConciseIdentity(); - } - - /** - * The method in the concrete item subclass that actually reads in the data for the item - * - * The logic in this method can assume that the given Input object is valid and is - * aligned as neccessary. - * - * This method is for internal use only - * @param in the Input object to read from - * @param readContext a ReadContext object to hold information that is - * only needed while reading in a file - */ - protected abstract void readItem(Input in, ReadContext readContext); - - /** - * The method should finalize the layout of the item and return the offset of the byte - * immediately following the item. - * - * The implementation of this method can assume that the offset argument has already been - * aligned based on the item's alignment requirements - * - * This method is for internal use only - * @param offset the (pre-aligned) offset to place the item at - * @return the size of the item, in bytes - */ - protected abstract int placeItem(int offset); - - /** - * The method in the concrete item subclass that actually writes and annotates the data - * for the item. - * - * The logic in this method can assume that the given Output object is valid and is - * aligned as neccessary - * - * @param out The AnnotatedOutput object to write/annotate to - */ - protected abstract void writeItem(AnnotatedOutput out); - - /** - * This method is called to add item specific context information to an exception, to identify the "current item" - * when the exception occured. It adds the value returned by getConciseIdentity as context for the - * exception - * @param ex The exception that occured - * @return A RuntimeException with additional details about the item added - */ - protected final RuntimeException addExceptionContext(Exception ex) { - return ExceptionWithContext.withContext(ex, getConciseIdentity()); - } - - /** - * @return An ItemType enum that represents the item type of this item - */ - public abstract ItemType getItemType(); - - /** - * @return A concise (human-readable) string value that conveys the identity of this item - */ - public abstract String getConciseIdentity(); - - - /** - * Note that the item must have been placed before calling this method (See DexFile.place()) - * @return the offset in the dex file where this item is located - */ - public int getOffset() { - Preconditions.checkState(offset != -1, - "The offset is not set until the DexFile containing this item is placed."); - return offset; - } - - /** - * Note that the item must have been placed before calling this method (See DexFile.place()) - * @return the index of this item within the item's containing section. - */ - public int getIndex() { - Preconditions.checkState(index != -1, - "The index is not set until the DexFile containing this item is placed."); - return index; - } - - /** - * @return True if this item has been placed, otherwise False - */ - public boolean isPlaced() { - return offset != -1; - } - - /** - * @return the DexFile that contains this item - */ - public DexFile getDexFile() { - return dexFile; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java deleted file mode 100644 index 553d1898..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemFactory.java +++ /dev/null @@ -1,71 +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; - -class ItemFactory { - protected static Item makeItem(ItemType itemType, DexFile dexFile) { - switch (itemType) { - case TYPE_STRING_ID_ITEM: - return new StringIdItem(dexFile); - case TYPE_TYPE_ID_ITEM: - return new TypeIdItem(dexFile); - case TYPE_PROTO_ID_ITEM: - return new ProtoIdItem(dexFile); - case TYPE_FIELD_ID_ITEM: - return new FieldIdItem(dexFile); - case TYPE_METHOD_ID_ITEM: - return new MethodIdItem(dexFile); - case TYPE_CLASS_DEF_ITEM: - return new ClassDefItem(dexFile); - case TYPE_TYPE_LIST: - return new TypeListItem(dexFile); - case TYPE_ANNOTATION_SET_REF_LIST: - return new AnnotationSetRefList(dexFile); - case TYPE_ANNOTATION_SET_ITEM: - return new AnnotationSetItem(dexFile); - case TYPE_CLASS_DATA_ITEM: - return new ClassDataItem(dexFile); - case TYPE_CODE_ITEM: - return new CodeItem(dexFile); - case TYPE_STRING_DATA_ITEM: - return new StringDataItem(dexFile); - case TYPE_DEBUG_INFO_ITEM: - return new DebugInfoItem(dexFile); - case TYPE_ANNOTATION_ITEM: - return new AnnotationItem(dexFile); - case TYPE_ENCODED_ARRAY_ITEM: - return new EncodedArrayItem(dexFile); - case TYPE_ANNOTATIONS_DIRECTORY_ITEM: - return new AnnotationDirectoryItem(dexFile); - default: - assert false; - } - return null; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemType.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemType.java deleted file mode 100644 index a8c7868f..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ItemType.java +++ /dev/null @@ -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.dexlib; - -import java.util.TreeMap; - -/** - * Enumeration of all the top-level item types. - */ -public enum ItemType { - TYPE_HEADER_ITEM( 0x0000, 17, 4, "header_item"), - TYPE_STRING_ID_ITEM( 0x0001, 0, 4, "string_id_item"), - TYPE_TYPE_ID_ITEM( 0x0002, 1, 4, "type_id_item"), - TYPE_PROTO_ID_ITEM( 0x0003, 2, 4, "proto_id_item"), - TYPE_FIELD_ID_ITEM( 0x0004, 3, 4, "field_id_item"), - TYPE_METHOD_ID_ITEM( 0x0005, 4, 4, "method_id_item"), - TYPE_CLASS_DEF_ITEM( 0x0006, 5, 4, "class_def_item"), - TYPE_MAP_LIST( 0x1000, 16, 4, "map_list"), - TYPE_TYPE_LIST( 0x1001, 6, 4, "type_list"), - TYPE_ANNOTATION_SET_REF_LIST( 0x1002, 7, 4, "annotation_set_ref_list"), - TYPE_ANNOTATION_SET_ITEM( 0x1003, 8, 4, "annotation_set_item"), - TYPE_CLASS_DATA_ITEM( 0x2000, 9, 1, "class_data_item"), - TYPE_CODE_ITEM( 0x2001, 10, 4, "code_item"), - TYPE_STRING_DATA_ITEM( 0x2002, 11, 1, "string_data_item"), - TYPE_DEBUG_INFO_ITEM( 0x2003, 12, 1, "debug_info_item"), - TYPE_ANNOTATION_ITEM( 0x2004, 13, 1, "annotation_item"), - TYPE_ENCODED_ARRAY_ITEM( 0x2005, 14, 1, "encoded_array_item"), - TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, 15, 4, "annotations_directory_item"); - - /** A map to facilitate looking up an ItemType by ordinal */ - private final static TreeMap itemTypeIntegerMap; - - /** builds the itemTypeIntegerMap object */ - static { - itemTypeIntegerMap = new TreeMap(); - - for (ItemType itemType: ItemType.values()) { - itemTypeIntegerMap.put(itemType.MapValue, itemType); - } - } - - - - /** - * value when represented in a MapItem - */ - public final int MapValue; - - /** - * name of the type - */ - public final String TypeName; - - /** - * index for this item's section - */ - public final int SectionIndex; - - /** - * the alignment for this item type - */ - public final int ItemAlignment; - /** - * Constructs an instance. - * - * @param mapValue value when represented in a MapItem - * @param sectionIndex index for this item's section - * @param itemAlignment the byte alignment required by this item - * @param typeName non-null; name of the type - */ - private ItemType(int mapValue, int sectionIndex, int itemAlignment, String typeName) { - this.MapValue = mapValue; - this.SectionIndex = sectionIndex; - this.ItemAlignment = itemAlignment; - this.TypeName = typeName; - } - - /** - * Converts an int value to the corresponding ItemType enum value, - * or null if the value isn't a valid ItemType value - * - * @param itemType the int value to convert to an ItemType - * @return the ItemType enum value corresponding to itemType, or null - * if not a valid ItemType value - */ - public static ItemType fromInt(int itemType) { - return itemTypeIntegerMap.get(itemType); - } - - /** - * Returns true if this is an indexed item, or false if its an offsetted item - * @return true if this is an indexed item, or false if its an offsetted item - */ - public boolean isIndexedItem() { - return MapValue <= 0x1000; - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MapItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MapItem.java deleted file mode 100644 index e69a33a4..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MapItem.java +++ /dev/null @@ -1,138 +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; - -/** - * This item represents a map_list item from the dex specification. It contains a - * SectionInfo instance for every section in the DexFile, with the number of items - * in and offset of that section. - */ -public class MapItem extends Item { - /** - * This item is read in immediately after the HeaderItem, and the section info contained - * by this item is added to the ReadContext object, which is used when reading in the other - * sections in the dex file. - * - * This item should be placed last. It depends on the fact that the other sections - * in the file have been placed. - */ - - /** - * Create a new uninitialized MapItem - * @param dexFile The DexFile that this item belongs to - */ - protected MapItem(final DexFile dexFile) { - super(dexFile); - } - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - Section[] sections = dexFile.getOrderedSections(); - //the list returned by getOrderedSections doesn't contain the header - //or map section, so add 2 to the length - return offset + 4 + (sections.length + 2) * 12; - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - int size = in.readInt(); - - for (int i=0; i 0; - Section[] sections = dexFile.getOrderedSections(); - - out.annotate("map_size: 0x" + Integer.toHexString(sections.length + 2) + " (" + - Integer.toString(sections.length + 2) + ")"); - out.writeInt(sections.length + 2); - - int index = 0; - out.annotate(0, "[" + index++ + "]"); - out.indent(); - writeSectionInfo(out, ItemType.TYPE_HEADER_ITEM, 1, 0); - out.deindent(); - - for (Section section: dexFile.getOrderedSections()) { - out.annotate(0, "[" + index++ + "]"); - out.indent(); - writeSectionInfo(out, section.ItemType, section.getItems().size(), section.getOffset()); - out.deindent(); - } - - out.annotate(0, "[" + index++ + "]"); - out.indent(); - writeSectionInfo(out, ItemType.TYPE_MAP_LIST, 1, dexFile.MapItem.getOffset()); - out.deindent(); - } - - private void writeSectionInfo(AnnotatedOutput out, ItemType itemType, int sectionSize, int sectionOffset) { - if (out.annotates()) { - out.annotate(2, "item_type: " + itemType); - out.annotate(2, "unused"); - out.annotate(4, "section_size: 0x" + Integer.toHexString(sectionSize) + " (" + sectionSize + ")"); - out.annotate(4, "section_off: 0x" + Integer.toHexString(sectionOffset)); - } - - out.writeShort(itemType.MapValue); - out.writeShort(0); - out.writeInt(sectionSize); - out.writeInt(sectionOffset); - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_MAP_LIST; - } - - /** {@inheritDoc} */ - public int compareTo(MapItem o) { - return 0; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "map_item"; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java deleted file mode 100644 index c3522c5e..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/MethodIdItem.java +++ /dev/null @@ -1,256 +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; - -public class MethodIdItem extends Item implements Convertible { - private int hashCode = 0; - - private TypeIdItem classType; - private ProtoIdItem methodPrototype; - private StringIdItem methodName; - - /** - * Creates a new uninitialized MethodIdItem - * @param dexFile The DexFile that this item belongs to - */ - protected MethodIdItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new MethodIdItem for the given class, type and name - * @param dexFile The DexFile that this item belongs to - * @param classType the class that the method is a member of - * @param methodPrototype the type of the method - * @param methodName the name of the method - */ - private MethodIdItem(DexFile dexFile, TypeIdItem classType, ProtoIdItem methodPrototype, StringIdItem methodName) { - this(dexFile); - this.classType = classType; - this.methodPrototype = methodPrototype; - this.methodName = methodName; - } - - /** - * Returns a MethodIdItem for the given values, and that has been interned into - * the given DexFile - * @param dexFile The DexFile that this item belongs to - * @param classType the class that the method is a member of - * @param methodPrototype the type of the method - * @param methodName the name of the method - * @return a MethodIdItem for the given values, and that has been interned into - * the given DexFile - */ - public static MethodIdItem internMethodIdItem(DexFile dexFile, TypeIdItem classType, - ProtoIdItem methodPrototype, StringIdItem methodName) { - MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName); - return dexFile.MethodIdsSection.intern(methodIdItem); - } - - /** - * Looks up a MethodIdItem from the given DexFile for the given - * values - * @param dexFile The DexFile that this item belongs to - * @param classType the class that the method is a member of - * @param methodPrototype the type of the method - * @param methodName the name of the method - * @return a MethodIdItem from the given DexFile for the given - * values, or null if it doesn't exist - */ - public static MethodIdItem lookupMethodIdItem(DexFile dexFile, TypeIdItem classType, - ProtoIdItem methodPrototype, StringIdItem methodName) { - MethodIdItem methodIdItem = new MethodIdItem(dexFile, classType, methodPrototype, methodName); - return dexFile.MethodIdsSection.getInternedItem(methodIdItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort()); - methodPrototype = dexFile.ProtoIdsSection.getItemByIndex(in.readShort()); - methodName = dexFile.StringIdsSection.getItemByIndex(in.readInt()); - } - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - return offset + 8; - } - - /** {@inheritDoc} */ - protected void writeItem(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate(2, "class_type: " + classType.getTypeDescriptor()); - out.annotate(2, "method_prototype: " + methodPrototype.getPrototypeString()); - out.annotate(4, "method_name: " + methodName.getStringValue()); - } - - int classIndex = classType.getIndex(); - if (classIndex > 0xffff) { - throw new RuntimeException(String.format("Error writing method_id_item for %s. The type index of " + - "defining class %s is too large", getMethodString(), classType.getTypeDescriptor())); - } - out.writeShort(classIndex); - - int prototypeIndex = methodPrototype.getIndex(); - if (prototypeIndex > 0xffff) { - throw new RuntimeException(String.format("Error writing method_id_item for %0. The prototype index of " + - "method prototype %s is too large", getMethodString(), methodPrototype.getPrototypeString())); - } - out.writeShort(prototypeIndex); - - out.writeInt(methodName.getIndex()); - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_METHOD_ID_ITEM; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "method_id_item: " + getMethodString(); - } - - /** {@inheritDoc} */ - public int compareTo(MethodIdItem o) { - int result = classType.compareTo(o.classType); - if (result != 0) { - return result; - } - - result = methodName.compareTo(o.methodName); - if (result != 0) { - return result; - } - - return methodPrototype.compareTo(o.methodPrototype); - } - - private String cachedMethodString = null; - /** - * @return a string formatted like LclassName;->methodName(TTTT..)R - */ - public String getMethodString() { - if (cachedMethodString == null) { - String classType = this.classType.getTypeDescriptor(); - String methodName = this.methodName.getStringValue(); - String prototypeString = methodPrototype.getPrototypeString(); - - StringBuilder sb = new StringBuilder(classType.length() + methodName.length() + prototypeString.length() + - 2); - sb.append(classType); - sb.append("->"); - sb.append(methodName); - sb.append(prototypeString); - cachedMethodString = sb.toString(); - } - return cachedMethodString; - } - - private String cachedShortMethodString = null; - /** - * @return a string formatted like methodName(TTTT..)R - */ - public String getShortMethodString() { - if (cachedShortMethodString == null) { - String methodName = this.methodName.getStringValue(); - String prototypeString = methodPrototype.getPrototypeString(); - - StringBuilder sb = new StringBuilder(methodName.length() + prototypeString.length()); - sb.append(methodName); - sb.append(prototypeString); - cachedShortMethodString = sb.toString(); - } - return cachedShortMethodString; - } - - /** - * @return the method prototype - */ - public ProtoIdItem getPrototype() { - return methodPrototype; - } - - /** - * @return the name of the method - */ - public StringIdItem getMethodName() { - return methodName; - } - - /** - * @return the class this method is a member of - */ - public TypeIdItem getContainingClass() { - return classType; - } - - /** - * calculate and cache the hashcode - */ - private void calcHashCode() { - hashCode = classType.hashCode(); - hashCode = 31 * hashCode + methodPrototype.hashCode(); - hashCode = 31 * hashCode + methodName.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; - } - - //This assumes that the referenced items have been interned in both objects. - //This is a valid assumption because all outside code must use the static - //"getInterned..." style methods to make new items, and any item created - //internally is guaranteed to be interned - MethodIdItem other = (MethodIdItem)o; - return (classType == other.classType && - methodPrototype == other.methodPrototype && - methodName == other.methodName); - } - - public MethodIdItem convert() { - return this; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java deleted file mode 100644 index 581b8acc..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/OdexDependencies.java +++ /dev/null @@ -1,76 +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; - -import org.jf.dexlib.Util.Input; - -import java.io.UnsupportedEncodingException; - -public class OdexDependencies { - public final int modificationTime; - public final int crc; - public final int dalvikBuild; - - private final String[] dependencies; - private final byte[][] dependencyChecksums; - - public OdexDependencies (Input in) { - modificationTime = in.readInt(); - crc = in.readInt(); - dalvikBuild = in.readInt(); - - int dependencyCount = in.readInt(); - - dependencies = new String[dependencyCount]; - dependencyChecksums = new byte[dependencyCount][]; - - for (int i=0; i extends Section { - public OffsettedSection(DexFile dexFile, ItemType itemType) { - super(dexFile, itemType); - } - - public void readItems(Input in, ReadContext readContext) { - - for (int i = 0; i < items.size(); i++) { - assert items.get(i) == null; - - in.alignTo(ItemType.ItemAlignment); - - T item = (T)ItemFactory.makeItem(ItemType, DexFile); - - items.set(i, item); - item.readFrom(in, i, readContext); - } - - readContext.setItemsForSection(ItemType, items); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java deleted file mode 100644 index a7b4a0a1..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ProtoIdItem.java +++ /dev/null @@ -1,231 +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; - -public class ProtoIdItem extends Item { - private int hashCode = 0; - - private StringIdItem shortyDescriptor; - private TypeIdItem returnType; - private TypeListItem parameters; - - /** - * Creates a new uninitialized ProtoIdItem - * @param dexFile The DexFile that this item belongs to - */ - protected ProtoIdItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new ProtoIdItem with the given values - * @param dexFile The DexFile that this item belongs to - * @param returnType the return type - * @param parameters a TypeListItem containing a list of the parameter types - */ - private ProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { - this(dexFile); - - String shortyString = returnType.toShorty(); - if (parameters != null) { - shortyString += parameters.getShortyString(); - } - this.shortyDescriptor = StringIdItem.internStringIdItem(dexFile, shortyString); - this.returnType = returnType; - this.parameters = parameters; - } - - /** - * Returns a ProtoIdItem for the given values, and that has been interned into - * the given DexFile - * @param dexFile The DexFile that this item belongs to - * @param returnType the return type - * @param parameters a TypeListItem containing a list of the parameter types - * @return a ProtoIdItem for the given values, and that has been interned into - * the given DexFile - */ - public static ProtoIdItem internProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { - ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters); - return dexFile.ProtoIdsSection.intern(protoIdItem); - } - - /** - * Looks up the ProtoIdItem from the given DexFile for the given - * values - * @param dexFile the Dexfile to find the type in - * @param returnType the return type - * @param parameters a TypeListItem containing a list of the parameter types - * @return a ProtoIdItem from the given DexFile for the given - * values, or null if it doesn't exist - */ - public static ProtoIdItem lookupProtoIdItem(DexFile dexFile, TypeIdItem returnType, TypeListItem parameters) { - ProtoIdItem protoIdItem = new ProtoIdItem(dexFile, returnType, parameters); - return dexFile.ProtoIdsSection.getInternedItem(protoIdItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - shortyDescriptor = dexFile.StringIdsSection.getItemByIndex(in.readInt()); - returnType = dexFile.TypeIdsSection.getItemByIndex(in.readInt()); - parameters = (TypeListItem)readContext.getOptionalOffsettedItemByOffset(ItemType.TYPE_TYPE_LIST, in.readInt()); - } - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - return offset + 12; - } - - /** {@inheritDoc} */ - protected void writeItem(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate(4, "shorty_descriptor: " + shortyDescriptor.getStringValue()); - out.annotate(4, "return_type: " + returnType.getTypeDescriptor()); - - if (parameters == null) { - out.annotate(4, "parameters:"); - } else { - out.annotate(4, "parameters: " + parameters.getTypeListString("")); - } - } - - out.writeInt(shortyDescriptor.getIndex()); - out.writeInt(returnType.getIndex()); - out.writeInt(parameters == null?0:parameters.getOffset()); - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_PROTO_ID_ITEM; - } - - /** {@inheritDoc} */ - public int compareTo(ProtoIdItem o) { - int result = returnType.compareTo(o.returnType); - if (result != 0) { - return result; - } - - if (parameters == null) { - if (o.parameters == null) { - return 0; - } - return -1; - } else if (o.parameters == null) { - return 1; - } - - return parameters.compareTo(o.parameters); - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "proto_id_item: " + getPrototypeString(); - } - - private String cachedPrototypeString = null; - /** - * @return a string in the format (TTTT..)R where TTTT.. are the parameter types and R is the return type - */ - public String getPrototypeString() { - if (cachedPrototypeString == null) { - StringBuilder sb = new StringBuilder("("); - if (parameters != null) { - sb.append(parameters.getTypeListString("")); - } - sb.append(")"); - sb.append(returnType.getTypeDescriptor()); - - cachedPrototypeString = sb.toString(); - } - return cachedPrototypeString; - } - - /** - * @return the return type of the method - */ - public TypeIdItem getReturnType() { - return returnType; - } - - /** - * @return a TypeListItem containing the method parameter types - */ - public TypeListItem getParameters() { - return parameters; - } - - /** - * @return the number of registers required for the parameters of this ProtoIdItem - */ - public int getParameterRegisterCount() { - if (parameters == null) { - return 0; - } else { - return parameters.getRegisterCount(); - } - } - - /** - * calculate and cache the hashcode - */ - private void calcHashCode() { - hashCode = returnType.hashCode(); - hashCode = 31 * hashCode + (parameters==null?0:parameters.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; - } - - //This assumes that the referenced items have been interned in both objects. - //This is a valid assumption because all outside code must use the static - //"getInterned..." style methods to make new items, and any item created - //internally is guaranteed to be interned - ProtoIdItem other = (ProtoIdItem)o; - return (returnType == other.returnType && - parameters == other.parameters); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ReadContext.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ReadContext.java deleted file mode 100644 index a25f3a27..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/ReadContext.java +++ /dev/null @@ -1,200 +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.ExceptionWithContext; -import org.jf.dexlib.Util.SparseArray; - -import java.util.List; - - -/** - * This class stores context information that is only needed when reading in a dex file - * Namely, it handles "pre-creating" items when an item needs to resolve some other item - * that it references, and keeps track of those pre-created items, so the corresponding section - * for the pre-created items uses them, instead of creating new items - */ -public class ReadContext { - private SparseArray typeListItems = new SparseArray(0); - private SparseArray annotationSetRefLists = new SparseArray(0); - private SparseArray annotationSetItems = new SparseArray(0); - private SparseArray classDataItems = new SparseArray(0); - private SparseArray codeItems = new SparseArray(0); - private SparseArray stringDataItems = new SparseArray(0); - private SparseArray debugInfoItems = new SparseArray(0); - private SparseArray annotationItems = new SparseArray(0); - private SparseArray encodedArrayItems = new SparseArray(0); - private SparseArray annotationDirectoryItems = new SparseArray(0); - - private SparseArray[] itemsByType = new SparseArray[] { - null, //string_id_item - null, //type_id_item - null, //proto_id_item - null, //field_id_item - null, //method_id_item - null, //class_def_item - typeListItems, - annotationSetRefLists, - annotationSetItems, - classDataItems, - codeItems, - stringDataItems, - debugInfoItems, - annotationItems, - encodedArrayItems, - annotationDirectoryItems, - null, //map_list - null //header_item - }; - - - /** - * The section sizes that are passed in while reading HeaderItem/MapItem, via the - * addSection method. - */ - private int[] sectionSizes = new int[18]; - - /** - * The section offsets that are passed in while reading MapItem/HeaderItem, via the - * addSection method. - */ - private int[] sectionOffsets = new int[18]; - - /** - * Creates a new ReadContext instance. - */ - public ReadContext() { - for (int i=0; i<18; i++) { - sectionSizes[i] = -1; - sectionOffsets[i] = -1; - } - } - - /** - * Gets the offsetted item of the specified type for the given offset. This method does not support retrieving an - * optional item where a value of 0 indicates "not present". Use getOptionalOffsettedItemByOffset instead. - * - * @param itemType The type of item to get - * @param offset The offset of the item - * @return the offsetted item of the specified type at the specified offset - */ - public Item getOffsettedItemByOffset(ItemType itemType, int offset) { - assert !itemType.isIndexedItem(); - - SparseArray sa = itemsByType[itemType.SectionIndex]; - Item item = sa.get(offset); - if (item == null) { - throw new ExceptionWithContext(String.format("Could not find the %s item at offset %#x", - itemType.TypeName, offset)); - } - return item; - } - - /** - * Gets the optional offsetted item of the specified type for the given offset - * - * @param itemType The type of item to get - * @param offset The offset of the item - * @return the offsetted item of the specified type at the specified offset, or null if the offset is 0 - */ - public Item getOptionalOffsettedItemByOffset(ItemType itemType, int offset) { - assert !itemType.isIndexedItem(); - - assert !itemType.isIndexedItem(); - - SparseArray sa = itemsByType[itemType.SectionIndex]; - Item item = sa.get(offset); - if (item == null && offset != 0) { - throw new ExceptionWithContext(String.format("Could not find the %s item at offset %#x", - itemType.TypeName, offset)); - } - return item; - } - - /** - * Adds the size and offset information for the given offset - * @param itemType the item type of the section - * @param sectionSize the size of the section - * @param sectionOffset the offset of the section - */ - public void addSection(final ItemType itemType, int sectionSize, int sectionOffset) { - int storedSectionSize = sectionSizes[itemType.SectionIndex]; - if (storedSectionSize == -1) { - sectionSizes[itemType.SectionIndex] = sectionSize; - } else { - if (storedSectionSize != sectionSize) { - throw new RuntimeException("The section size in the header and map for item type " - + itemType + " do not match"); - } - } - - int storedSectionOffset = sectionOffsets[itemType.SectionIndex]; - if (storedSectionOffset == -1) { - sectionOffsets[itemType.SectionIndex] = sectionOffset; - } else { - if (storedSectionOffset != sectionOffset) { - throw new RuntimeException("The section offset in the header and map for item type " - + itemType + " do not match"); - } - } - } - - /** - * Sets the items for the specified section. This should be called by an offsetted section - * after it is finished reading in all its items. - * @param itemType the item type of the section. This must be an offsetted item type - * @param items the full list of items in the section, ordered by offset - */ - public void setItemsForSection(ItemType itemType, List items) { - assert !itemType.isIndexedItem(); - - SparseArray sa = itemsByType[itemType.SectionIndex]; - - sa.ensureCapacity(items.size()); - for (Item item: items) { - sa.append(item.getOffset(), item); - } - } - - /** - * @param itemType the item type of the section - * @return the size of the given section as it was read in from the map item - */ - public int getSectionSize(ItemType itemType) { - return sectionSizes[itemType.SectionIndex]; - } - - /** - * @param itemType the item type of the section - * @return the offset of the given section as it was read in from the map item - */ - public int getSectionOffset(ItemType itemType) { - return sectionOffsets[itemType.SectionIndex]; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Section.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Section.java deleted file mode 100644 index 0c20a719..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Section.java +++ /dev/null @@ -1,221 +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.AlignmentUtils; -import org.jf.dexlib.Util.AnnotatedOutput; -import org.jf.dexlib.Util.Input; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -public abstract class Section { - /** - * A list of the items that this section contains. - * If the section has been placed, this list should be in the order that the items - * will written to the dex file - */ - protected final ArrayList items; - - /** - * A HashMap of the items in this section. This is used when interning items, to determine - * if this section already has an item equivalent to the one that is being interned. - * Both the key and the value should be the same object - */ - protected HashMap uniqueItems = null; - - /** - * The offset of this section within the DexFile - */ - protected int offset = 0; - - /** - * The type of item that this section holds - */ - public final ItemType ItemType; - - /** - * The DexFile that this section belongs to - */ - public final DexFile DexFile; - - /** - * Create a new section - * @param dexFile The DexFile that this section belongs to - * @param itemType The itemType that this section will hold - */ - protected Section(DexFile dexFile, ItemType itemType) { - this.DexFile = dexFile; - items = new ArrayList(); - this.ItemType = itemType; - } - - /** - * Finalize the location of all items, and place them starting at the given offset - * @param offset The offset where this section should be placed - * @return the offset of the byte immediate after the last item in this section - */ - protected int placeAt(int offset) { - if (items.size() > 0) { - offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment); - assert !DexFile.getInplace() || offset == this.offset; - this.offset = offset; - - for (int i=0; i < items.size(); i++) { - T item = items.get(i); - assert item != null; - offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment); - offset = item.placeAt(offset, i); - } - } else { - this.offset = 0; - } - - return offset; - } - - /** - * Write the items to the given AnnotatedOutput - * @param out the AnnotatedOutput object to write to - */ - protected void writeTo(AnnotatedOutput out) { - out.annotate(0, " "); - out.annotate(0, "-----------------------------"); - out.annotate(0, this.ItemType.TypeName + " section"); - out.annotate(0, "-----------------------------"); - out.annotate(0, " "); - - for (Item item: items) { - assert item!=null; - out.alignTo(ItemType.ItemAlignment); - item.writeTo(out); - out.annotate(0, " "); - } - } - - /** - * Read the specified number of items from the given Input object - * @param size The number of items to read - * @param in The Input object to read from - * @param readContext a ReadContext object to hold information that is - * only needed while reading in a file - */ - protected void readFrom(int size, Input in, ReadContext readContext) { - //readItems() expects that the list will already be the correct size, so add null items - //until we reach the specified size - items.ensureCapacity(size); - for (int i = items.size(); i < size; i++) { - items.add(null); - } - - in.alignTo(ItemType.ItemAlignment); - offset = in.getCursor(); - - //call the subclass's method that actually reads in the items - readItems(in, readContext); - } - - /** - * This method in the concrete item subclass should read in all the items from the given Input - * object, using any pre-created items as applicable (i.e. items that were created prior to reading in the - * section, by other items requesting items from this section that they reference by index/offset) - * @param in the Input - * @param readContext a ReadContext object to hold information that is - * only needed while reading in a file - */ - protected abstract void readItems(Input in, ReadContext readContext); - - /** - * Gets the offset where the first item in this section is placed - * @return the ofset where the first item in this section is placed - */ - public int getOffset() { - return offset; - } - - /** - * Gets a the items contained in this section as a read-only list - * @return A read-only List object containing the items in this section - */ - public List getItems() { - return Collections.unmodifiableList(items); - } - - /** - * This method checks if an item that is equivalent to the given item has already been added. If found, - * it returns that item. If not found, it adds the given item to this section and returns it. - * @param item the item to intern - * @return An item from this section that is equivalent to the given item. It may or may not be the same - * as the item passed to this method. - */ - protected T intern(T item) { - if (item == null) { - return null; - } - T internedItem = getInternedItem(item); - if (internedItem == null) { - uniqueItems.put(item, item); - items.add(item); - return item; - } - return internedItem; - } - - /** - * Returns the interned item that is equivalent to the given item, or null - * @param item the item to check - * @return the interned item that is equivalent to the given item, or null - */ - protected T getInternedItem(T item) { - if (uniqueItems == null) { - buildInternedItemMap(); - } - return uniqueItems.get(item); - } - - /** - * Builds the interned item map from the items that are in this section - */ - private void buildInternedItemMap() { - uniqueItems = new HashMap(); - for (T item: items) { - assert item != null; - uniqueItems.put(item, item); - } - } - - /** - * Sorts the items in the section - */ - protected void sortSection() { - Collections.sort(items); - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java deleted file mode 100644 index a565d099..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringDataItem.java +++ /dev/null @@ -1,169 +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 org.jf.dexlib.Util.Leb128Utils; -import org.jf.dexlib.Util.Utf8Utils; - -public class StringDataItem extends Item { - private int hashCode = 0; - - private String stringValue; - - /** - * Creates a new uninitialized StringDataItem - * @param dexFile The DexFile that this item belongs to - */ - protected StringDataItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new StringDataItem for the given string - * @param dexFile The DexFile that this item belongs to - * @param stringValue The string value that this item represents - */ - private StringDataItem(DexFile dexFile, String stringValue) { - super(dexFile); - - this.stringValue = stringValue; - } - - /** - * Returns a StringDataItem for the given values, and that has been interned into - * the given DexFile - * @param dexFile The DexFile that this item belongs to - * @param value The string value that this item represents - * @return a StringDataItem for the given values, and that has been interned into - * the given DexFile - */ - public static StringDataItem internStringDataItem(DexFile dexFile, String value) { - StringDataItem StringDataItem = new StringDataItem(dexFile, value); - return dexFile.StringDataSection.intern(StringDataItem); - } - - /** - * Looks up the StringDataItem from the given DexFile for the given - * string value - * @param dexFile the Dexfile to find the string value in - * @param value The string value to look up - * @return a StringDataItem from the given DexFile for the given - * string value, or null if it doesn't exist - **/ - public static StringDataItem lookupStringDataItem(DexFile dexFile, String value) { - StringDataItem StringDataItem = new StringDataItem(dexFile, value); - return dexFile.StringDataSection.getInternedItem(StringDataItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - in.readUnsignedLeb128(); //string length - stringValue = in.realNullTerminatedUtf8String(); - } - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - return offset + Leb128Utils.unsignedLeb128Size(stringValue.length()) + - Utf8Utils.stringToUtf8Bytes(stringValue).length + 1; - } - - /** {@inheritDoc} */ - protected void writeItem(AnnotatedOutput out) { - byte[] encodedValue = Utf8Utils.stringToUtf8Bytes(stringValue); - if (out.annotates()) { - out.annotate("string_size: 0x" + Integer.toHexString(stringValue.length()) + " (" + stringValue.length() + - ")"); - out.writeUnsignedLeb128(stringValue.length()); - - out.annotate(encodedValue.length + 1, "string_data: \"" + Utf8Utils.escapeString(stringValue) + "\""); - } else { - out.writeUnsignedLeb128(stringValue.length()); - } - out.write(encodedValue); - out.writeByte(0); - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_STRING_DATA_ITEM; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "string_data_item: \"" + Utf8Utils.escapeString(getStringValue()) + "\""; - } - - /** {@inheritDoc} */ - public int compareTo(StringDataItem o) { - return getStringValue().compareTo(o.getStringValue()); - } - - /** - * Get the string value of this item as a String - * @return the string value of this item as a String - */ - public String getStringValue() { - return stringValue; - } - - /** - * calculate and cache the hashcode - */ - private void calcHashCode() { - hashCode = getStringValue().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; - } - - //This assumes that the referenced items have been interned in both objects. - //This is a valid assumption because all outside code must use the static - //"getInterned..." style methods to make new items, and any item created - //internally is guaranteed to be interned - StringDataItem other = (StringDataItem)o; - return getStringValue().equals(other.getStringValue()); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java deleted file mode 100644 index 61bb65ef..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/StringIdItem.java +++ /dev/null @@ -1,177 +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 org.jf.dexlib.Util.Utf8Utils; - -import javax.annotation.Nullable; - -public class StringIdItem extends Item { - private StringDataItem stringDataItem; - - /** - * Creates a new uninitialized StringIdItem - * @param dexFile The DexFile that this item belongs to - */ - protected StringIdItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new StringIdItem for the given StringDataItem - * @param dexFile The DexFile that this item belongs to - * @param stringDataItem The StringDataItem that this StringIdItem represents - */ - protected StringIdItem(DexFile dexFile, StringDataItem stringDataItem) { - super(dexFile); - this.stringDataItem = stringDataItem; - } - - /** - * Returns a StringIdItem for the given values, and that has been interned into - * the given DexFile - * @param dexFile The DexFile that this item will belong to - * @param stringValue The string value that this item represents - * @return a StringIdItem for the given values, and that has been interned into - * the given DexFile - */ - public static StringIdItem internStringIdItem(DexFile dexFile, String stringValue) { - StringDataItem stringDataItem = StringDataItem.internStringDataItem(dexFile, stringValue); - if (stringDataItem == null) { - return null; - } - StringIdItem stringIdItem = new StringIdItem(dexFile, stringDataItem); - return dexFile.StringIdsSection.intern(stringIdItem); - } - - /** - * Looks up the StringIdItem from the given DexFile for the given - * string value - * @param dexFile the Dexfile to find the string value in - * @param stringValue The string value to look up - * @return a StringIdItem from the given DexFile for the given - * string value, or null if it doesn't exist - */ - public static StringIdItem lookupStringIdItem(DexFile dexFile, String stringValue) { - StringDataItem stringDataItem = StringDataItem.lookupStringDataItem(dexFile, stringValue); - if (stringDataItem == null) { - return null; - } - StringIdItem stringIdItem = new StringIdItem(dexFile, stringDataItem); - return dexFile.StringIdsSection.getInternedItem(stringIdItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - int stringDataOffset = in.readInt(); - - stringDataItem = (StringDataItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_STRING_DATA_ITEM, - stringDataOffset); - } - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - return offset + 4; - } - - /** {@inheritDoc} */ - protected void writeItem(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate(4, stringDataItem.getConciseIdentity()); - } - - out.writeInt(stringDataItem.getOffset()); - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_STRING_ID_ITEM; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "string_id_item: " + Utf8Utils.escapeString(getStringValue()); - } - - /** {@inheritDoc} */ - public int compareTo(StringIdItem o) { - //sort by the string value - return getStringValue().compareTo(o.getStringValue()); - } - - /** - * Get the String value that this StringIdItem represents - * @return the String value that this StringIdItem represents - */ - public String getStringValue() { - return stringDataItem.getStringValue(); - } - - /** - * Get the String value that the given StringIdItem represents - * @param stringIdItem The StringIdItem to get the string value of - * @return the String value that the given StringIdItem represents - */ - @Nullable - public static String getStringValue(@Nullable StringIdItem stringIdItem) { - return stringIdItem==null?null:stringIdItem.getStringValue(); - } - - /** - * Get the StringDataItem that this StringIdItem references - * @return the StringDataItem that this StringIdItem references - */ - public StringDataItem getStringDataItem() { - return stringDataItem; - } - - @Override - public int hashCode() { - return stringDataItem.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this==o) { - return true; - } - if (o==null || !this.getClass().equals(o.getClass())) { - return false; - } - - //This assumes that the referenced items have been interned in both objects. - //This is a valid assumption because all outside code must use the static - //"getInterned..." style methods to make new items, and any item created - //internally is guaranteed to be interned - StringIdItem other = (StringIdItem)o; - return stringDataItem == other.stringDataItem; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java deleted file mode 100644 index 6cc2b968..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeIdItem.java +++ /dev/null @@ -1,211 +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 javax.annotation.Nullable; - -public class TypeIdItem extends Item { - private StringIdItem typeDescriptor; - - /** - * Creates a new uninitialized TypeIdItem - * @param dexFile The DexFile that this item belongs to - */ - protected TypeIdItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new TypeIdItem for the given StringIdItem - * @param dexFile The DexFile that this item will belong to - * @param typeDescriptor The StringIdItem containing the type descriptor that - * this TypeIdItem represents - */ - private TypeIdItem(DexFile dexFile, StringIdItem typeDescriptor) { - super(dexFile); - this.typeDescriptor = typeDescriptor; - } - - /** - * Returns a TypeIdItem for the given values, and that has been interned into - * the given DexFile - * @param dexFile The DexFile that this item will belong to - * @param typeDescriptor The StringIdItem containing the type descriptor that - * this TypeIdItem represents - * @return a TypeIdItem for the given values, and that has been interned into - * the given DexFile - */ - public static TypeIdItem internTypeIdItem(DexFile dexFile, StringIdItem typeDescriptor) { - TypeIdItem typeIdItem = new TypeIdItem(dexFile, typeDescriptor); - return dexFile.TypeIdsSection.intern(typeIdItem); - } - - /** - * Returns a TypeIdItem for the given values, and that has been interned into - * the given DexFile - * @param dexFile The DexFile that this item will belong to - * @param typeDescriptor The string containing the type descriptor that this - * TypeIdItem represents - * @return a TypeIdItem for the given values, and that has been interned into - * the given DexFile - */ - public static TypeIdItem internTypeIdItem(DexFile dexFile, String typeDescriptor) { - StringIdItem stringIdItem = StringIdItem.internStringIdItem(dexFile, typeDescriptor); - if (stringIdItem == null) { - return null; - } - TypeIdItem typeIdItem = new TypeIdItem(dexFile, stringIdItem); - return dexFile.TypeIdsSection.intern(typeIdItem); - } - - /** - * Looks up the TypeIdItem from the given DexFile for the given - * type descriptor - * @param dexFile the Dexfile to find the type in - * @param typeDescriptor The string containing the type descriptor to look up - * @return a TypeIdItem from the given DexFile for the given - * type descriptor, or null if it doesn't exist - */ - public static TypeIdItem lookupTypeIdItem(DexFile dexFile, String typeDescriptor) { - StringIdItem stringIdItem = StringIdItem.lookupStringIdItem(dexFile, typeDescriptor); - if (stringIdItem == null) { - return null; - } - TypeIdItem typeIdItem = new TypeIdItem(dexFile, stringIdItem); - return dexFile.TypeIdsSection.getInternedItem(typeIdItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - int stringIdIndex = in.readInt(); - this.typeDescriptor = dexFile.StringIdsSection.getItemByIndex(stringIdIndex); - } - - /** {@inheritDoc} */ - protected int placeItem(int offset) { - return offset + 4; - } - - /** {@inheritDoc} */ - protected void writeItem(AnnotatedOutput out) { - if (out.annotates()) { - out.annotate(4, typeDescriptor.getConciseIdentity()); - } - - out.writeInt(typeDescriptor.getIndex()); - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_TYPE_ID_ITEM; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "type_id_item: " + getTypeDescriptor(); - } - - /** {@inheritDoc} */ - public int compareTo(TypeIdItem o) { - //sort by the index of the StringIdItem - return typeDescriptor.compareTo(o.typeDescriptor); - } - - /** - * Returns the type descriptor as a String for this type - * @return the type descriptor as a String for this type - */ - public String getTypeDescriptor() { - return typeDescriptor.getStringValue(); - } - - /** - * Returns the type descriptor as a String for the given type - * @param typeIdItem The TypeIdItem to get the type descriptor of - * @return the type descriptor as a String for the gvien type - */ - @Nullable - public static String getTypeDescriptor(@Nullable TypeIdItem typeIdItem) { - return typeIdItem==null?null:typeIdItem.getTypeDescriptor(); - } - - /** - * Returns the "shorty" representation of this type, used to create the shorty prototype string for a method - * @return the "shorty" representation of this type, used to create the shorty prototype string for a method - */ - public String toShorty() { - String type = getTypeDescriptor(); - if (type.length() > 1) { - return "L"; - } else { - return type; - } - } - - /** - * Calculates the number of 2-byte registers that an instance of this type requires - * @return The number of 2-byte registers that an instance of this type requires - */ - public int getRegisterCount() { - String type = this.getTypeDescriptor(); - /** Only the long and double primitive types are 2 words, - * everything else is a single word - */ - if (type.charAt(0) == 'J' || type.charAt(0) == 'D') { - return 2; - } else { - return 1; - } - } - - @Override - public int hashCode() { - return typeDescriptor.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (this==o) { - return true; - } - if (o==null || !this.getClass().equals(o.getClass())) { - return false; - } - - //This assumes that the referenced items have been interned in both objects. - //This is a valid assumption because all outside code must use the static - //"getInterned..." style methods to make new items, and any item created - //internally is guaranteed to be interned - TypeIdItem other = (TypeIdItem)o; - return typeDescriptor == other.typeDescriptor; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java deleted file mode 100644 index 50262ea1..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/TypeListItem.java +++ /dev/null @@ -1,288 +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 org.jf.dexlib.Util.ReadOnlyArrayList; - -import java.util.List; - -public class TypeListItem extends Item { - private int hashCode = 0; - - private TypeIdItem[] typeList; - - /** - * Creates a new uninitialized TypeListItem - * @param dexFile The DexFile that this item belongs to - */ - protected TypeListItem(DexFile dexFile) { - super(dexFile); - } - - /** - * Creates a new TypeListItem for the given string - * @param dexFile The DexFile that this item belongs to - * @param typeList A list of the types that this TypeListItem represents - */ - private TypeListItem(DexFile dexFile, TypeIdItem[] typeList) { - super(dexFile); - - this.typeList = typeList; - } - - /** - * Returns a TypeListItem for the given values, and that has been interned into - * the given DexFile - * @param dexFile The DexFile that this item belongs to - * @param typeList A list of the types that this TypeListItem represents - * @return a TypeListItem for the given values, and that has been interned into - * the given DexFile - */ - public static TypeListItem internTypeListItem(DexFile dexFile, List typeList) { - TypeIdItem[] typeArray = new TypeIdItem[typeList.size()]; - typeList.toArray(typeArray); - TypeListItem typeListItem = new TypeListItem(dexFile, typeArray); - return dexFile.TypeListsSection.intern(typeListItem); - } - - /** - * Looks up the TypeListItem from the given DexFile for the given - * list of types - * @param dexFile the Dexfile to find the type in - * @param typeList A list of the types that the TypeListItem represents - * @return a TypeListItem from the given DexFile for the given - * list of types, or null if it doesn't exist - */ - public static TypeListItem lookupTypeListItem(DexFile dexFile, List typeList) { - TypeIdItem[] typeArray = new TypeIdItem[typeList.size()]; - typeList.toArray(typeArray); - TypeListItem typeListItem = new TypeListItem(dexFile, typeArray); - return dexFile.TypeListsSection.getInternedItem(typeListItem); - } - - /** {@inheritDoc} */ - protected void readItem(Input in, ReadContext readContext) { - int size = in.readInt(); - typeList = new TypeIdItem[size]; - for (int i=0; i 0xffff) { - throw new RuntimeException(String.format("Error writing type_list entry. The type index of " + - "type %s is too large", typeIdItem.getTypeDescriptor())); - } - out.writeShort(typeIndex); - } - } - - /** {@inheritDoc} */ - public ItemType getItemType() { - return ItemType.TYPE_TYPE_LIST; - } - - /** {@inheritDoc} */ - public String getConciseIdentity() { - return "type_list: " + getTypeListString(""); - } - - /** {@inheritDoc} */ - public int compareTo(TypeListItem o) { - if (o == null) { - return 1; - } - - int thisSize = typeList.length; - int otherSize = o.typeList.length; - int size = Math.min(thisSize, otherSize); - - for (int i = 0; i < size; i++) { - int result = typeList[i].compareTo(o.typeList[i]); - if (result != 0) { - return result; - } - } - - if (thisSize < otherSize) { - return -1; - } else if (thisSize > otherSize) { - return 1; - } else { - return 0; - } - } - - /** - * @return the number of registers required for this TypeListItem - */ - public int getRegisterCount() { - int wordCount = 0; - for (TypeIdItem typeIdItem: typeList) { - wordCount += typeIdItem.getRegisterCount(); - } - return wordCount; - } - - /** - * @return a string consisting of the type descriptors in this TypeListItem - * that are separated by the given separator - * @param separator the separator between each type - */ - public String getTypeListString(String separator) { - int size = 0; - for (TypeIdItem typeIdItem: typeList) { - size += typeIdItem.getTypeDescriptor().length(); - size += separator.length(); - } - - StringBuilder sb = new StringBuilder(size); - for (TypeIdItem typeIdItem: typeList) { - sb.append(typeIdItem.getTypeDescriptor()); - sb.append(separator); - } - if (typeList.length > 0) { - sb.delete(sb.length() - separator.length(), sb.length()); - } - return sb.toString(); - } - - /** - * @return a string consisting of the shorty form of the type descriptors in this - * TypeListItem that are directly concatenated together - */ - public String getShortyString() { - StringBuilder sb = new StringBuilder(); - for (TypeIdItem typeIdItem: typeList) { - sb.append(typeIdItem.toShorty()); - } - return sb.toString(); - } - - /** - * @param index the index of the TypeIdItem to get - * @return the TypeIdItem at the given index - */ - public TypeIdItem getTypeIdItem(int index) { - return typeList[index]; - } - - /** - * @return the number of types in this TypeListItem - */ - public int getTypeCount() { - return typeList.length; - } - - /** - * @return an array of the TypeIdItems in this TypeListItem - */ - public List getTypes() { - return new ReadOnlyArrayList(typeList); - } - - /** - * Helper method to allow easier "inline" retrieval of of the list of TypeIdItems - * @param typeListItem the typeListItem to return the types of (can be null) - * @return an array of the TypeIdItems in the specified TypeListItem, or null if the - * TypeListItem is null - */ - public static List getTypes(TypeListItem typeListItem) { - return typeListItem==null?null:typeListItem.getTypes(); - } - - /** - * calculate and cache the hashcode - */ - private void calcHashCode() { - int hashCode = 1; - - for (TypeIdItem typeIdItem: typeList) { - hashCode = 31 * hashCode + typeIdItem.hashCode(); - } - this.hashCode = 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; - } - - //This assumes that the referenced items have been interned in both objects. - //This is a valid assumption because all outside code must use the static - //"getInterned..." style methods to make new items, and any item created - //internally is guaranteed to be interned - TypeListItem other = (TypeListItem)o; - if (typeList.length != other.typeList.length) { - return false; - } - - for (int i=0; itrue iff annotations are being kept - */ - public boolean annotates(); - - /** - * Get whether this instance is intended to keep verbose annotations. - * Annotators may use the result of calling this method to inform their - * annotation activity. - * - * @return true iff annotations are to be verbose - */ - public boolean isVerbose(); - - /** - * Add an annotation for the subsequent output. Any previously - * open annotation will be closed by this call, and the new - * annotation marks all subsequent output until another annotation - * call. - * - * @param msg non-null; the annotation message - */ - public void annotate(String msg); - - /** - * Add an annotation for a specified amount of subsequent - * output. Any previously open annotation will be closed by this - * call. If there is already pending annotation from one or more - * previous calls to this method, the new call "consumes" output - * after all the output covered by the previous calls. - * - * @param amt >= 0; the amount of output for this annotation to - * cover - * @param msg non-null; the annotation message - */ - public void annotate(int amt, String msg); - - /** - * End the most recent annotation. Subsequent output will be unannotated, - * until the next call to {@link #annotate}. - */ - public void endAnnotation(); - - /** - * Get the maximum width of the annotated output. This is advisory: - * Implementations of this interface are encouraged to deal with too-wide - * output, but annotaters are encouraged to attempt to avoid exceeding - * the indicated width. - * - * @return >= 1; the maximum width - */ - public int getAnnotationWidth(); - - public void setIndentAmount(int indentAmount); - public void indent(); - public void deindent(); -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java deleted file mode 100644 index dab08372..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ArrayUtils.java +++ /dev/null @@ -1,71 +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.Util; - -import java.util.Arrays; -import java.util.Comparator; - -public class ArrayUtils { - /** - * Utility method to sort two related arrays - that is, two arrays where the elements are related to each other - * by their position in the array. i.e. firstArray[0] is related to secondArray[0], firstArray[1] is related to - * secondArray[1], and so on. The first array is sorted based on its implementation of Comparable, and the 2nd - * array is sorted by it's related item in the first array, so that the same elements are still related to each - * other after the sort - * @param firstArray The first array, which contains the values to sort - * @param secondArray The second array, which will be sorted based on the values in the first array - * @param The type of element in the first array - * @param the type of element in the second array - */ - public static , B> void sortTwoArrays(A[] firstArray, B[] secondArray) - { - if (firstArray.length != secondArray.length) { - throw new RuntimeException("Both arrays must be of the same length"); - } - - class element - { - public A first; - public B second; - } - - element[] elements = new element[firstArray.length]; - - Arrays.sort(elements, new Comparator(){ - public int compare(element a, element b) { - return a.first.compareTo(b.first); - } - }); - - for (int i=0; ibyte[], which provides read-only access and - * can "reveal" a partial slice of the underlying array. - * - * Note: Multibyte accessors all use big-endian order. - */ -public final class ByteArray { - /** non-null; underlying array */ - private final byte[] bytes; - - /** >= 0; start index of the slice (inclusive) */ - private final int start; - - /** >= 0, <= bytes.length; size computed as - * end - start (in the constructor) */ - private final int size; - - /** - * Constructs an instance. - * - * @param bytes non-null; the underlying array - * @param start >= 0; start index of the slice (inclusive) - * @param end >= start, <= bytes.length; end index of - * the slice (exclusive) - */ - public ByteArray(byte[] bytes, int start, int end) { - if (bytes == null) { - throw new NullPointerException("bytes == null"); - } - - if (start < 0) { - throw new IllegalArgumentException("start < 0"); - } - - if (end < start) { - throw new IllegalArgumentException("end < start"); - } - - if (end > bytes.length) { - throw new IllegalArgumentException("end > bytes.length"); - } - - this.bytes = bytes; - this.start = start; - this.size = end - start; - } - - /** - * Constructs an instance from an entire byte[]. - * - * @param bytes non-null; the underlying array - */ - public ByteArray(byte[] bytes) { - this(bytes, 0, bytes.length); - } - - /** - * Gets the size of the array, in bytes. - * - * @return >= 0; the size - */ - public int size() { - return size; - } - - /** - * Returns a slice (that is, a sub-array) of this instance. - * - * @param start >= 0; start index of the slice (inclusive) - * @param end >= start, <= size(); end index of - * the slice (exclusive) - * @return non-null; the slice - */ - public ByteArray slice(int start, int end) { - checkOffsets(start, end); - return new ByteArray(bytes, start + this.start, end + this.start); - } - - /** - * Returns the offset into the given array represented by the given - * offset into this instance. - * - * @param offset offset into this instance - * @param bytes non-null; (alleged) underlying array - * @return corresponding offset into bytes - * @throws IllegalArgumentException thrown if bytes is - * not the underlying array of this instance - */ - public int underlyingOffset(int offset, byte[] bytes) { - if (bytes != this.bytes) { - throw new IllegalArgumentException("wrong bytes"); - } - - return start + offset; - } - - /** - * Gets the signed byte value at a particular offset. - * - * @param off >= 0, < size(); offset to fetch - * @return signed byte at that offset - */ - public int getByte(int off) { - checkOffsets(off, off + 1); - return getByte0(off); - } - - /** - * Gets the signed short value at a particular offset. - * - * @param off >= 0, < (size() - 1); offset to fetch - * @return signed short at that offset - */ - public int getShort(int off) { - checkOffsets(off, off + 2); - return (getByte0(off) << 8) | getUnsignedByte0(off + 1); - } - - /** - * Gets the signed int value at a particular offset. - * - * @param off >= 0, < (size() - 3); offset to fetch - * @return signed int at that offset - */ - public int getInt(int off) { - checkOffsets(off, off + 4); - return (getByte0(off) << 24) | - (getUnsignedByte0(off + 1) << 16) | - (getUnsignedByte0(off + 2) << 8) | - getUnsignedByte0(off + 3); - } - - /** - * Gets the signed long value at a particular offset. - * - * @param off >= 0, < (size() - 7); offset to fetch - * @return signed int at that offset - */ - public long getLong(int off) { - checkOffsets(off, off + 8); - int part1 = (getByte0(off) << 24) | - (getUnsignedByte0(off + 1) << 16) | - (getUnsignedByte0(off + 2) << 8) | - getUnsignedByte0(off + 3); - int part2 = (getByte0(off + 4) << 24) | - (getUnsignedByte0(off + 5) << 16) | - (getUnsignedByte0(off + 6) << 8) | - getUnsignedByte0(off + 7); - - return (part2 & 0xffffffffL) | ((long) part1) << 32; - } - - /** - * Gets the unsigned byte value at a particular offset. - * - * @param off >= 0, < size(); offset to fetch - * @return unsigned byte at that offset - */ - public int getUnsignedByte(int off) { - checkOffsets(off, off + 1); - return getUnsignedByte0(off); - } - - /** - * Gets the unsigned short value at a particular offset. - * - * @param off >= 0, < (size() - 1); offset to fetch - * @return unsigned short at that offset - */ - public int getUnsignedShort(int off) { - checkOffsets(off, off + 2); - return (getUnsignedByte0(off) << 8) | getUnsignedByte0(off + 1); - } - - /** - * Copies the contents of this instance into the given raw - * byte[] at the given offset. The given array must be - * large enough. - * - * @param out non-null; array to hold the output - * @param offset non-null; index into out for the first - * byte of output - */ - public void getBytes(byte[] out, int offset) { - if ((out.length - offset) < size) { - throw new IndexOutOfBoundsException("(out.length - offset) < " + - "size()"); - } - - System.arraycopy(bytes, start, out, offset, size); - } - - /** - * Checks a range of offsets for validity, throwing if invalid. - * - * @param s start offset (inclusive) - * @param e end offset (exclusive) - */ - private void checkOffsets(int s, int e) { - if ((s < 0) || (e < s) || (e > size)) { - throw new IllegalArgumentException("bad range: " + s + ".." + e + - "; actual size " + size); - } - } - - /** - * Gets the signed byte value at the given offset, - * without doing any argument checking. - * - * @param off offset to fetch - * @return byte at that offset - */ - private int getByte0(int off) { - return bytes[start + off]; - } - - /** - * Gets the unsigned byte value at the given offset, - * without doing any argument checking. - * - * @param off offset to fetch - * @return byte at that offset - */ - private int getUnsignedByte0(int off) { - return bytes[start + off] & 0xff; - } -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java deleted file mode 100644 index ae33cb0e..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayAnnotatedOutput.java +++ /dev/null @@ -1,678 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * As per the Apache license requirements, this file has been modified - * from its original state. - * - * Such modifications are Copyright (C) 2010 Ben Gruver, and are released - * under the original license - */ - -package org.jf.dexlib.Util; - -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; - -/** - * Implementation of {@link AnnotatedOutput} which stores the written data - * into a byte[]. - * - *

    Note: As per the {@link Output} interface, multi-byte - * writes all use little-endian order.

    - */ -public final class ByteArrayAnnotatedOutput - implements AnnotatedOutput { - /** default size for stretchy instances */ - private static final int DEFAULT_SIZE = 1000; - - /** - * whether the instance is stretchy, that is, whether its array - * may be resized to increase capacity - */ - private final boolean stretchy; - - /** non-null; the data itself */ - private byte[] data; - - /** >= 0; current output cursor */ - private int cursor; - - /** whether annotations are to be verbose */ - private boolean verbose; - - /** - * null-ok; list of annotations, or null if this instance - * isn't keeping them - */ - private ArrayList annotations; - - /** >= 40 (if used); the desired maximum annotation width */ - private int annotationWidth; - - /** - * >= 8 (if used); the number of bytes of hex output to use - * in annotations - */ - private int hexCols; - - private int currentIndent = 0; - private int indentAmount = 2; - - /** - * Constructs an instance with a fixed maximum size. Note that the - * given array is the only one that will be used to store data. In - * particular, no reallocation will occur in order to expand the - * capacity of the resulting instance. Also, the constructed - * instance does not keep annotations by default. - * - * @param data non-null; data array to use for output - */ - public ByteArrayAnnotatedOutput(byte[] data) { - this(data, false); - } - - /** - * Constructs a "stretchy" instance. The underlying array may be - * reallocated. The constructed instance does not keep annotations - * by default. - */ - public ByteArrayAnnotatedOutput() { - this(new byte[DEFAULT_SIZE], true); - } - - /** - * Internal constructor. - * - * @param data non-null; data array to use for output - * @param stretchy whether the instance is to be stretchy - */ - private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) { - if (data == null) { - throw new NullPointerException("data == null"); - } - - this.stretchy = stretchy; - this.data = data; - this.cursor = 0; - this.verbose = false; - this.annotations = null; - this.annotationWidth = 0; - this.hexCols = 0; - } - - /** - * Gets the underlying byte[] of this instance, which - * may be larger than the number of bytes written - * - * @see #toByteArray - * - * @return non-null; the byte[] - */ - public byte[] getArray() { - return data; - } - - /** - * Constructs and returns a new byte[] that contains - * the written contents exactly (that is, with no extra unwritten - * bytes at the end). - * - * @see #getArray - * - * @return non-null; an appropriately-constructed array - */ - public byte[] toByteArray() { - byte[] result = new byte[cursor]; - System.arraycopy(data, 0, result, 0, cursor); - return result; - } - - /** {@inheritDoc} */ - public int getCursor() { - return cursor; - } - - /** {@inheritDoc} */ - public void assertCursor(int expectedCursor) { - if (cursor != expectedCursor) { - throw new ExceptionWithContext("expected cursor " + - expectedCursor + "; actual value: " + cursor); - } - } - - /** {@inheritDoc} */ - public void writeByte(int value) { - int writeAt = cursor; - int end = writeAt + 1; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - data[writeAt] = (byte) value; - cursor = end; - } - - /** {@inheritDoc} */ - public void writeShort(int value) { - int writeAt = cursor; - int end = writeAt + 2; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - data[writeAt] = (byte) value; - data[writeAt + 1] = (byte) (value >> 8); - cursor = end; - } - - /** {@inheritDoc} */ - public void writeInt(int value) { - int writeAt = cursor; - int end = writeAt + 4; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - data[writeAt] = (byte) value; - data[writeAt + 1] = (byte) (value >> 8); - data[writeAt + 2] = (byte) (value >> 16); - data[writeAt + 3] = (byte) (value >> 24); - cursor = end; - } - - /** {@inheritDoc} */ - public void writeLong(long value) { - int writeAt = cursor; - int end = writeAt + 8; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - int half = (int) value; - data[writeAt] = (byte) half; - data[writeAt + 1] = (byte) (half >> 8); - data[writeAt + 2] = (byte) (half >> 16); - data[writeAt + 3] = (byte) (half >> 24); - - half = (int) (value >> 32); - data[writeAt + 4] = (byte) half; - data[writeAt + 5] = (byte) (half >> 8); - data[writeAt + 6] = (byte) (half >> 16); - data[writeAt + 7] = (byte) (half >> 24); - - cursor = end; - } - - /** {@inheritDoc} */ - public int writeUnsignedLeb128(int value) { - long remaining = (value & 0xFFFFFFFFL) >> 7; - long lValue = value; - int count = 0; - - while (remaining != 0) { - writeByte((int)(lValue & 0x7f) | 0x80); - lValue = remaining; - remaining >>= 7; - count++; - } - - writeByte((int)(lValue & 0x7f)); - return count + 1; - } - - /** {@inheritDoc} */ - public int writeSignedLeb128(int value) { - int remaining = value >> 7; - int count = 0; - boolean hasMore = true; - int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; - - while (hasMore) { - hasMore = (remaining != end) - || ((remaining & 1) != ((value >> 6) & 1)); - - writeByte((value & 0x7f) | (hasMore ? 0x80 : 0)); - value = remaining; - remaining >>= 7; - count++; - } - - return count; - } - - /** {@inheritDoc} */ - public void write(ByteArray bytes) { - int blen = bytes.size(); - int writeAt = cursor; - int end = writeAt + blen; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - bytes.getBytes(data, writeAt); - cursor = end; - } - - /** {@inheritDoc} */ - public void write(byte[] bytes, int offset, int length) { - int writeAt = cursor; - int end = writeAt + length; - int bytesEnd = offset + length; - - // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0) - if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) { - throw new IndexOutOfBoundsException("bytes.length " + - bytes.length + "; " + - offset + "..!" + end); - } - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - System.arraycopy(bytes, offset, data, writeAt, length); - cursor = end; - } - - /** {@inheritDoc} */ - public void write(byte[] bytes) { - write(bytes, 0, bytes.length); - } - - /** {@inheritDoc} */ - public void writeZeroes(int count) { - if (count < 0) { - throw new IllegalArgumentException("count < 0"); - } - - int end = cursor + count; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - /* - * There is no need to actually write zeroes, since the array is - * already preinitialized with zeroes. - */ - - cursor = end; - } - - /** {@inheritDoc} */ - public void alignTo(int alignment) { - int mask = alignment - 1; - - if ((alignment < 0) || ((mask & alignment) != 0)) { - throw new IllegalArgumentException("bogus alignment"); - } - - int end = (cursor + mask) & ~mask; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - /* - * There is no need to actually write zeroes, since the array is - * already preinitialized with zeroes. - */ - - cursor = end; - } - - /** {@inheritDoc} */ - public boolean annotates() { - return (annotations != null); - } - - /** {@inheritDoc} */ - public boolean isVerbose() { - return verbose; - } - - /** {@inheritDoc} */ - public void annotate(String msg) { - if (annotations == null) { - return; - } - - endAnnotation(); - annotations.add(new Annotation(cursor, msg, currentIndent)); - } - - public void indent() { - currentIndent++; - } - - public void deindent() { - currentIndent--; - if (currentIndent < 0) { - currentIndent = 0; - } - } - - public void setIndentAmount(int indentAmount) { - this.indentAmount = indentAmount; - } - - /** {@inheritDoc} */ - public void annotate(int amt, String msg) { - if (annotations == null) { - return; - } - - endAnnotation(); - - int asz = annotations.size(); - int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd(); - int startAt; - - if (lastEnd <= cursor) { - startAt = cursor; - } else { - startAt = lastEnd; - } - - annotations.add(new Annotation(startAt, startAt + amt, msg, currentIndent)); - } - - /** {@inheritDoc} */ - public void endAnnotation() { - if (annotations == null) { - return; - } - - int sz = annotations.size(); - - if (sz != 0) { - annotations.get(sz - 1).setEndIfUnset(cursor); - } - } - - /** {@inheritDoc} */ - public int getAnnotationWidth() { - int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); - - return annotationWidth - leftWidth; - } - - /** - * Indicates that this instance should keep annotations. This method may - * be called only once per instance, and only before any data has been - * written to the it. - * - * @param annotationWidth >= 40; the desired maximum annotation width - * @param verbose whether or not to indicate verbose annotations - */ - public void enableAnnotations(int annotationWidth, boolean verbose) { - if ((annotations != null) || (cursor != 0)) { - throw new RuntimeException("cannot enable annotations"); - } - - if (annotationWidth < 40) { - throw new IllegalArgumentException("annotationWidth < 40"); - } - - int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1; - if (hexCols < 6) { - hexCols = 6; - } else if (hexCols > 10) { - hexCols = 10; - } - - this.annotations = new ArrayList(1000); - this.annotationWidth = annotationWidth; - this.hexCols = hexCols; - this.verbose = verbose; - } - - /** - * Finishes up annotation processing. This closes off any open - * annotations and removes annotations that don't refer to written - * data. - */ - public void finishAnnotating() { - // Close off the final annotation, if any. - endAnnotation(); - - // Remove annotations that refer to unwritten data. - if (annotations != null) { - int asz = annotations.size(); - while (asz > 0) { - Annotation last = annotations.get(asz - 1); - if (last.getStart() > cursor) { - annotations.remove(asz - 1); - asz--; - } else if (last.getEnd() > cursor) { - last.setEnd(cursor); - break; - } else { - break; - } - } - } - } - - /** - * Writes the annotated content of this instance to the given writer. - * - * @param out non-null; where to write to - */ - public void writeAnnotationsTo(Writer out) throws IOException { - int width2 = getAnnotationWidth(); - int width1 = annotationWidth - width2 - 1; - - StringBuilder padding = new StringBuilder(); - for (int i=0; i<1000; i++) { - padding.append(' '); - } - - TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|"); - Writer left = twoc.getLeft(); - Writer right = twoc.getRight(); - int leftAt = 0; // left-hand byte output cursor - int rightAt = 0; // right-hand annotation index - int rightSz = annotations.size(); - - while ((leftAt < cursor) && (rightAt < rightSz)) { - Annotation a = annotations.get(rightAt); - int start = a.getStart(); - int end; - String text; - - if (leftAt < start) { - // This is an area with no annotation. - end = start; - start = leftAt; - text = ""; - } else { - // This is an area with an annotation. - end = a.getEnd(); - text = padding.substring(0, a.getIndent() * this.indentAmount) + a.getText(); - rightAt++; - } - - left.write(Hex.dump(data, start, end - start, start, hexCols, 6)); - right.write(text); - twoc.flush(); - leftAt = end; - } - - if (leftAt < cursor) { - // There is unannotated output at the end. - left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt, - hexCols, 6)); - } - - while (rightAt < rightSz) { - // There are zero-byte annotations at the end. - right.write(annotations.get(rightAt).getText()); - rightAt++; - } - - twoc.flush(); - } - - /** - * Throws the excpetion for when an attempt is made to write past the - * end of the instance. - */ - private static void throwBounds() { - throw new IndexOutOfBoundsException("attempt to write past the end"); - } - - /** - * Reallocates the underlying array if necessary. Calls to this method - * should be guarded by a test of {@link #stretchy}. - * - * @param desiredSize >= 0; the desired minimum total size of the array - */ - private void ensureCapacity(int desiredSize) { - if (data.length < desiredSize) { - byte[] newData = new byte[desiredSize * 2 + 1000]; - System.arraycopy(data, 0, newData, 0, cursor); - data = newData; - } - } - - /** - * Annotation on output. - */ - private static class Annotation { - /** >= 0; start of annotated range (inclusive) */ - private final int start; - - /** - * >= 0; end of annotated range (exclusive); - * Integer.MAX_VALUE if unclosed - */ - private int end; - - /** non-null; annotation text */ - private final String text; - - private int indent; - - /** - * Constructs an instance. - * - * @param start >= 0; start of annotated range - * @param end >= start; end of annotated range (exclusive) or - * Integer.MAX_VALUE if unclosed - * @param text non-null; annotation text - */ - public Annotation(int start, int end, String text, int indent) { - this.start = start; - this.end = end; - this.text = text; - this.indent = indent; - } - - /** - * Constructs an instance. It is initally unclosed. - * - * @param start >= 0; start of annotated range - * @param text non-null; annotation text - */ - public Annotation(int start, String text, int indent) { - this(start, Integer.MAX_VALUE, text, indent); - } - - /** - * Sets the end as given, but only if the instance is unclosed; - * otherwise, do nothing. - * - * @param end >= start; the end - */ - public void setEndIfUnset(int end) { - if (this.end == Integer.MAX_VALUE) { - this.end = end; - } - } - - /** - * Sets the end as given. - * - * @param end >= start; the end - */ - public void setEnd(int end) { - this.end = end; - } - - /** - * Gets the start. - * - * @return the start - */ - public int getStart() { - return start; - } - - /** - * Gets the end. - * - * @return the end - */ - public int getEnd() { - return end; - } - - /** - * Gets the text. - * - * @return non-null; the text - */ - public String getText() { - return text; - } - - public int getIndent() { - return indent; - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java deleted file mode 100644 index add68ee8..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * As per the Apache license requirements, this file has been modified - * from its original state. - * - * Such modifications are Copyright (C) 2010 Ben Gruver, and are released - * under the original license - */ - -package org.jf.dexlib.Util; - -/** - * Implementation of {@link Input} which reads the data from a - * byte[] instance. - * - *

    Note: As per the {@link Input } interface, multi-byte - * reads all use little-endian order.

    - */ -public class ByteArrayInput - implements Input { - - /** non-null; the data itself */ - private byte[] data; - - /** >= 0; current read cursor */ - private int cursor; - - /* buffer for reading UTF-8 strings */ - private char[] buffer = null; - - /** - * Constructs an instance with the given data - * - * @param data non-null; data array to use for input - */ - public ByteArrayInput(byte[] data) { - if (data == null) { - throw new NullPointerException("data == null"); - } - - this.data = data; - this.cursor = 0; - } - - /** - * Gets the underlying byte[] of this instance - * - * @return non-null; the byte[] - */ - public byte[] getArray() { - return data; - } - - /** {@inheritDoc} */ - public int getCursor() { - return cursor; - } - - /** {@inheritDoc} */ - public void setCursor(int cursor) { - if (cursor < 0 || cursor >= data.length) - throw new IndexOutOfBoundsException("The provided cursor value " + - "is not within the bounds of this instance's data array"); - this.cursor = cursor; - } - - /** {@inheritDoc} */ - public void assertCursor(int expectedCursor) { - if (cursor != expectedCursor) { - throw new ExceptionWithContext("expected cursor " + - expectedCursor + "; actual value: " + cursor); - } - } - - /** {@inheritDoc} */ - public byte readByte() { - return data[cursor++]; - } - - /** {@inheritDoc} */ - public int readShort() { - int readAt = cursor; - int result = ((data[readAt++] & 0xff) + - ((data[readAt++] & 0xff) << 8)); - cursor = readAt; - return result; - } - - /** {@inheritDoc} */ - public int readInt() { - int readAt = cursor; - int result = (data[readAt++] & 0xff) + - ((data[readAt++] & 0xff) << 8) + - ((data[readAt++] & 0xff) << 16) + - ((data[readAt++] & 0xff) << 24); - cursor = readAt; - return result; - } - - /** {@inheritDoc} */ - public long readLong() { - int readAt = cursor; - - long result = (data[readAt++] & 0xffL) | - ((data[readAt++] & 0xffL) << 8) | - ((data[readAt++] & 0xffL) << 16) | - ((data[readAt++] & 0xffL) << 24) | - ((data[readAt++] & 0xffL) << 32) | - ((data[readAt++] & 0xffL) << 40) | - ((data[readAt++] & 0xffL) << 48) | - ((data[readAt++] & 0xffL) << 56); - cursor = readAt; - return result; - } - - - /** {@inheritDoc} */ - public int readUnsignedOrSignedLeb128() { - int end = cursor; - int currentByteValue; - int result; - - result = data[end++] & 0xff; - if (result > 0x7f) { - currentByteValue = data[end++] & 0xff; - result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); - if (currentByteValue > 0x7f) { - currentByteValue = data[end++] & 0xff; - result |= (currentByteValue & 0x7f) << 14; - if (currentByteValue > 0x7f) { - currentByteValue = data[end++] & 0xff; - result |= (currentByteValue & 0x7f) << 21; - if (currentByteValue > 0x7f) { - currentByteValue = data[end++] & 0xff; - if (currentByteValue > 0x0f) { - throwInvalidLeb(); - } - result |= currentByteValue << 28; - } - } - } - } else { - cursor = end; - return result; - } - - cursor = end; - - //If the last byte is 0, then this was an unsigned value (incorrectly) written in a signed format - //The caller wants to know if this is the case, so we'll return the negated value instead - //If there was only a single byte that had a value of 0, then we would have returned in the above - //"else" - if (data[end-1] == 0) { - return ~result; - } - return result; - } - - - - - /** {@inheritDoc} */ - public int readUnsignedLeb128() { - int end = cursor; - int currentByteValue; - int result; - - result = data[end++] & 0xff; - if (result > 0x7f) { - currentByteValue = data[end++] & 0xff; - result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); - if (currentByteValue > 0x7f) { - currentByteValue = data[end++] & 0xff; - result |= (currentByteValue & 0x7f) << 14; - if (currentByteValue > 0x7f) { - currentByteValue = data[end++] & 0xff; - result |= (currentByteValue & 0x7f) << 21; - if (currentByteValue > 0x7f) { - currentByteValue = data[end++] & 0xff; - if (currentByteValue > 0x0f) { - throwInvalidLeb(); - } - result |= currentByteValue << 28; - } - } - } - } - - cursor = end; - return result; - } - - /** {@inheritDoc} */ - public int readSignedLeb128() { - int end = cursor; - int currentByteValue; - int result; - - result = data[end++] & 0xff; - if (result <= 0x7f) { - result = (result << 25) >> 25; - } else { - currentByteValue = data[end++] & 0xff; - result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); - if (currentByteValue <= 0x7f) { - result = (result << 18) >> 18; - } else { - currentByteValue = data[end++] & 0xff; - result |= (currentByteValue & 0x7f) << 14; - if (currentByteValue <= 0x7f) { - result = (result << 11) >> 11; - } else { - currentByteValue = data[end++] & 0xff; - result |= (currentByteValue & 0x7f) << 21; - if (currentByteValue <= 0x7f) { - result = (result << 4) >> 4; - } else { - currentByteValue = data[end++] & 0xff; - if (currentByteValue > 0x0f) { - throwInvalidLeb(); - } - result |= currentByteValue << 28; - } - } - } - } - - cursor = end; - return result; - } - - /** {@inheritDoc} */ - public void read(byte[] bytes, int offset, int length) { - int end = cursor + length; - - if (end > data.length) { - throwBounds(); - } - - System.arraycopy(data, cursor, bytes, offset, length); - cursor = end; - } - - /** {@inheritDoc} */ - public void read(byte[] bytes) { - int length = bytes.length; - int end = cursor + length; - - if (end > data.length) { - throwBounds(); - } - - System.arraycopy(data, cursor, bytes, 0, length); - cursor = end; - } - - /** {@inheritDoc} */ - public byte[] readBytes(int length) { - int end = cursor + length; - - if (end > data.length) { - throwBounds(); - } - - byte[] result = new byte[length]; - System.arraycopy(data, cursor, result, 0, length); - cursor = end; - return result; - } - - /** {@inheritDoc} */ - public String realNullTerminatedUtf8String() { - int startPosition = cursor; - while (data[cursor] != 0) { - cursor++; - } - int byteCount = cursor - startPosition; - - //skip the terminating null - cursor++; - - if (buffer == null || buffer.length < byteCount) { - buffer = new char[byteCount]; - } - - return Utf8Utils.utf8BytesToString(buffer, data, startPosition, byteCount); - } - - /** {@inheritDoc} */ - public void skipBytes(int count) { - cursor += count; - } - - /** {@inheritDoc} */ - public void alignTo(int alignment) { - cursor = AlignmentUtils.alignOffset(cursor, alignment); - } - - /** - * Throws the excpetion for when an attempt is made to read past the - * end of the instance. - */ - private static void throwBounds() { - throw new IndexOutOfBoundsException("attempt to read past the end"); - } - - /** - * Throws the exception for when an invalid LEB128 value is encountered - */ - private static void throwInvalidLeb() { - throw new RuntimeException("invalid LEB128 integer encountered"); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java deleted file mode 100644 index f2a1f709..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayOutput.java +++ /dev/null @@ -1,578 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * As per the Apache license requirements, this file has been modified - * from its original state. - * - * Such modifications are Copyright (C) 2010 Ben Gruver, and are released - * under the original license - */ - -package org.jf.dexlib.Util; - -import java.util.ArrayList; - -/** - * Implementation of {@link AnnotatedOutput} which stores the written data - * into a byte[]. - * - *

    Note: As per the {@link Output} interface, multi-byte - * writes all use little-endian order.

    - */ -public final class ByteArrayOutput implements Output -{ - /** default size for stretchy instances */ - private static final int DEFAULT_SIZE = 1000; - - /** - * whether the instance is stretchy, that is, whether its array - * may be resized to increase capacity - */ - private final boolean stretchy; - - /** non-null; the data itself */ - private byte[] data; - - /** >= 0; current output cursor */ - private int cursor; - - /** whether annotations are to be verbose */ - private boolean verbose; - - /** - * null-ok; list of annotations, or null if this instance - * isn't keeping them - */ - private ArrayList annotations; - - /** >= 40 (if used); the desired maximum annotation width */ - private int annotationWidth; - - /** - * >= 8 (if used); the number of bytes of hex output to use - * in annotations - */ - private int hexCols; - - /** - * Constructs an instance with a fixed maximum size. Note that the - * given array is the only one that will be used to store data. In - * particular, no reallocation will occur in order to expand the - * capacity of the resulting instance. Also, the constructed - * instance does not keep annotations by default. - * - * @param data non-null; data array to use for output - */ - public ByteArrayOutput(byte[] data) { - this(data, false); - } - - /** - * Constructs a "stretchy" instance. The underlying array may be - * reallocated. The constructed instance does not keep annotations - * by default. - */ - public ByteArrayOutput() { - this(new byte[DEFAULT_SIZE], true); - } - - /** - * Internal constructor. - * - * @param data non-null; data array to use for output - * @param stretchy whether the instance is to be stretchy - */ - private ByteArrayOutput(byte[] data, boolean stretchy) { - if (data == null) { - throw new NullPointerException("data == null"); - } - - this.stretchy = stretchy; - this.data = data; - this.cursor = 0; - this.verbose = false; - this.annotations = null; - this.annotationWidth = 0; - this.hexCols = 0; - } - - /** - * Gets the underlying byte[] of this instance, which - * may be larger than the number of bytes written - * - * @see #toByteArray - * - * @return non-null; the byte[] - */ - public byte[] getArray() { - return data; - } - - /** - * Constructs and returns a new byte[] that contains - * the written contents exactly (that is, with no extra unwritten - * bytes at the end). - * - * @see #getArray - * - * @return non-null; an appropriately-constructed array - */ - public byte[] toByteArray() { - byte[] result = new byte[cursor]; - System.arraycopy(data, 0, result, 0, cursor); - return result; - } - - /** {@inheritDoc} */ - public int getCursor() { - return cursor; - } - - /** {@inheritDoc} */ - public void assertCursor(int expectedCursor) { - if (cursor != expectedCursor) { - throw new ExceptionWithContext("expected cursor " + - expectedCursor + "; actual value: " + cursor); - } - } - - /** {@inheritDoc} */ - public void writeByte(int value) { - int writeAt = cursor; - int end = writeAt + 1; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - data[writeAt] = (byte) value; - cursor = end; - } - - /** {@inheritDoc} */ - public void writeShort(int value) { - int writeAt = cursor; - int end = writeAt + 2; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - data[writeAt] = (byte) value; - data[writeAt + 1] = (byte) (value >> 8); - cursor = end; - } - - /** {@inheritDoc} */ - public void writeInt(int value) { - int writeAt = cursor; - int end = writeAt + 4; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - data[writeAt] = (byte) value; - data[writeAt + 1] = (byte) (value >> 8); - data[writeAt + 2] = (byte) (value >> 16); - data[writeAt + 3] = (byte) (value >> 24); - cursor = end; - } - - /** {@inheritDoc} */ - public void writeLong(long value) { - int writeAt = cursor; - int end = writeAt + 8; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - int half = (int) value; - data[writeAt] = (byte) half; - data[writeAt + 1] = (byte) (half >> 8); - data[writeAt + 2] = (byte) (half >> 16); - data[writeAt + 3] = (byte) (half >> 24); - - half = (int) (value >> 32); - data[writeAt + 4] = (byte) half; - data[writeAt + 5] = (byte) (half >> 8); - data[writeAt + 6] = (byte) (half >> 16); - data[writeAt + 7] = (byte) (half >> 24); - - cursor = end; - } - - /** {@inheritDoc} */ - public int writeUnsignedLeb128(int value) { - int remaining = value >>> 7; - int count = 0; - - while (remaining != 0) { - writeByte((value & 0x7f) | 0x80); - value = remaining; - remaining >>>= 7; - count++; - } - - writeByte(value & 0x7f); - return count + 1; - } - - /** {@inheritDoc} */ - public int writeSignedLeb128(int value) { - int remaining = value >> 7; - int count = 0; - boolean hasMore = true; - int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; - - while (hasMore) { - hasMore = (remaining != end) - || ((remaining & 1) != ((value >> 6) & 1)); - - writeByte((value & 0x7f) | (hasMore ? 0x80 : 0)); - value = remaining; - remaining >>= 7; - count++; - } - - return count; - } - - /** {@inheritDoc} */ - public void write(ByteArray bytes) { - int blen = bytes.size(); - int writeAt = cursor; - int end = writeAt + blen; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - bytes.getBytes(data, writeAt); - cursor = end; - } - - /** {@inheritDoc} */ - public void write(byte[] bytes, int offset, int length) { - int writeAt = cursor; - int end = writeAt + length; - int bytesEnd = offset + length; - - // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0) - if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) { - throw new IndexOutOfBoundsException("bytes.length " + - bytes.length + "; " + - offset + "..!" + end); - } - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - System.arraycopy(bytes, offset, data, writeAt, length); - cursor = end; - } - - /** {@inheritDoc} */ - public void write(byte[] bytes) { - write(bytes, 0, bytes.length); - } - - /** {@inheritDoc} */ - public void writeZeroes(int count) { - if (count < 0) { - throw new IllegalArgumentException("count < 0"); - } - - int end = cursor + count; - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - - /* - * There is no need to actually write zeroes, since the array is - * already preinitialized with zeroes. - */ - - cursor = end; - } - - /** {@inheritDoc} */ - public void alignTo(int alignment) { - int end = AlignmentUtils.alignOffset(cursor, alignment); - - if (stretchy) { - ensureCapacity(end); - } else if (end > data.length) { - throwBounds(); - return; - } - cursor = end; - } - - /** {@inheritDoc} */ - public boolean annotates() { - return (annotations != null); - } - - /** {@inheritDoc} */ - public boolean isVerbose() { - return verbose; - } - - /** {@inheritDoc} */ - public void annotate(String msg) { - if (annotations == null) { - return; - } - - endAnnotation(); - annotations.add(new Annotation(cursor, msg)); - } - - /** {@inheritDoc} */ - public void annotate(int amt, String msg) { - if (annotations == null) { - return; - } - - endAnnotation(); - - int asz = annotations.size(); - int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd(); - int startAt; - - if (lastEnd <= cursor) { - startAt = cursor; - } else { - startAt = lastEnd; - } - - annotations.add(new Annotation(startAt, startAt + amt, msg)); - } - - /** {@inheritDoc} */ - public void endAnnotation() { - if (annotations == null) { - return; - } - - int sz = annotations.size(); - - if (sz != 0) { - annotations.get(sz - 1).setEndIfUnset(cursor); - } - } - - /** {@inheritDoc} */ - public int getAnnotationWidth() { - int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); - - return annotationWidth - leftWidth; - } - - /** - * Indicates that this instance should keep annotations. This method may - * be called only once per instance, and only before any data has been - * written to the it. - * - * @param annotationWidth >= 40; the desired maximum annotation width - * @param verbose whether or not to indicate verbose annotations - */ - public void enableAnnotations(int annotationWidth, boolean verbose) { - if ((annotations != null) || (cursor != 0)) { - throw new RuntimeException("cannot enable annotations"); - } - - if (annotationWidth < 40) { - throw new IllegalArgumentException("annotationWidth < 40"); - } - - int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1; - if (hexCols < 6) { - hexCols = 6; - } else if (hexCols > 10) { - hexCols = 10; - } - - this.annotations = new ArrayList(1000); - this.annotationWidth = annotationWidth; - this.hexCols = hexCols; - this.verbose = verbose; - } - - /** - * Finishes up annotation processing. This closes off any open - * annotations and removes annotations that don't refer to written - * data. - */ - public void finishAnnotating() { - // Close off the final annotation, if any. - endAnnotation(); - - // Remove annotations that refer to unwritten data. - if (annotations != null) { - int asz = annotations.size(); - while (asz > 0) { - Annotation last = annotations.get(asz - 1); - if (last.getStart() > cursor) { - annotations.remove(asz - 1); - asz--; - } else if (last.getEnd() > cursor) { - last.setEnd(cursor); - break; - } else { - break; - } - } - } - } - - /** - * Throws the excpetion for when an attempt is made to write past the - * end of the instance. - */ - private static void throwBounds() { - throw new IndexOutOfBoundsException("attempt to write past the end"); - } - - /** - * Reallocates the underlying array if necessary. Calls to this method - * should be guarded by a test of {@link #stretchy}. - * - * @param desiredSize >= 0; the desired minimum total size of the array - */ - private void ensureCapacity(int desiredSize) { - if (data.length < desiredSize) { - byte[] newData = new byte[desiredSize * 2 + 1000]; - System.arraycopy(data, 0, newData, 0, cursor); - data = newData; - } - } - - /** - * Annotation on output. - */ - private static class Annotation { - /** >= 0; start of annotated range (inclusive) */ - private final int start; - - /** - * >= 0; end of annotated range (exclusive); - * Integer.MAX_VALUE if unclosed - */ - private int end; - - /** non-null; annotation text */ - private final String text; - - /** - * Constructs an instance. - * - * @param start >= 0; start of annotated range - * @param end >= start; end of annotated range (exclusive) or - * Integer.MAX_VALUE if unclosed - * @param text non-null; annotation text - */ - public Annotation(int start, int end, String text) { - this.start = start; - this.end = end; - this.text = text; - } - - /** - * Constructs an instance. It is initally unclosed. - * - * @param start >= 0; start of annotated range - * @param text non-null; annotation text - */ - public Annotation(int start, String text) { - this(start, Integer.MAX_VALUE, text); - } - - /** - * Sets the end as given, but only if the instance is unclosed; - * otherwise, do nothing. - * - * @param end >= start; the end - */ - public void setEndIfUnset(int end) { - if (this.end == Integer.MAX_VALUE) { - this.end = end; - } - } - - /** - * Sets the end as given. - * - * @param end >= start; the end - */ - public void setEnd(int end) { - this.end = end; - } - - /** - * Gets the start. - * - * @return the start - */ - public int getStart() { - return start; - } - - /** - * Gets the end. - * - * @return the end - */ - public int getEnd() { - return end; - } - - /** - * Gets the text. - * - * @return non-null; the text - */ - public String getText() { - return text; - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java deleted file mode 100644 index 2bfd8cd6..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java +++ /dev/null @@ -1,451 +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.Util; - -import org.jf.dexlib.*; - -import java.util.ArrayList; -import java.util.List; - -/** - * This class is intended to provide an easy to use container to build up a method's debug info. You can easily add - * an "event" at a specific address, where an event is something like a line number, start/end local, etc. - * The events must be added such that the code addresses increase monotonically. This matches how a parser would - * generally behave, and is intended to increase performance. - */ -public class DebugInfoBuilder -{ - private static final int LINE_BASE = -4; - private static final int LINE_RANGE = 15; - private static final int FIRST_SPECIAL = 0x0a; - - private int lineStart = 0; - private ArrayList parameterNames = new ArrayList(); - private ArrayList events = new ArrayList(); - private int lastAddress = 0; - - private boolean hasData; - - private int currentAddress; - private int currentLine; - - public DebugInfoBuilder() { - } - - private void checkAddress(int address) { - if (lastAddress > address) { - throw new RuntimeException("Cannot add an event with an address before the address of the prior event"); - } - } - - public void addParameterName(String parameterName) { - if (parameterName != null) { - hasData = true; - } - - parameterNames.add(parameterName); - } - - public void addLine(int address, int line) { - hasData = true; - - checkAddress(address); - - if (lineStart == 0) { - lineStart = line; - } - - events.add(new LineEvent(address, line)); - } - - public void addLocal(int address, int registerNumber, String localName, String localType) { - hasData = true; - - checkAddress(address); - - events.add(new StartLocalEvent(address, registerNumber, localName, localType)); - } - - public void addLocalExtended(int address, int registerNumber, String localName, String localType, - String signature) { - hasData = true; - - checkAddress(address); - - events.add(new StartLocalExtendedEvent(address, registerNumber, localName, localType, signature)); - } - - public void addEndLocal(int address, int registerNumber) { - hasData = true; - - checkAddress(address); - - events.add(new EndLocalEvent(address, registerNumber)); - } - - public void addRestartLocal(int address, int registerNumber) { - hasData = true; - - checkAddress(address); - - events.add(new RestartLocalEvent(address, registerNumber)); - } - - public void addPrologue(int address) { - hasData = true; - - checkAddress(address); - - events.add(new PrologueEvent(address)); - } - - public void addEpilogue(int address) { - hasData = true; - - checkAddress(address); - - events.add(new EpilogueEvent(address)); - } - - public void addSetFile(int address, String fileName) { - hasData = true; - - checkAddress(address); - - events.add(new SetFileEvent(address, fileName)); - } - - public int getParameterNameCount() { - return parameterNames.size(); - } - - public DebugInfoItem encodeDebugInfo(DexFile dexFile) { - if (!hasData) { - return null; - } - - ByteArrayOutput out = new ByteArrayOutput(); - StringIdItem[] parameterNamesArray = new StringIdItem[parameterNames.size()]; - ArrayList referencedItems = new ArrayList(); - - if (lineStart == 0) { - lineStart = 1; - } - - currentLine = lineStart; - - for (Event event: events) { - event.emit(dexFile, out, referencedItems); - } - emitEndSequence(out); - - int index = 0; - for (String parameterName: parameterNames) { - if (parameterName == null) { - parameterNamesArray[index++] = null; - } else { - parameterNamesArray[index++] = StringIdItem.internStringIdItem(dexFile, parameterName); - } - } - - Item[] referencedItemsArray = new Item[referencedItems.size()]; - referencedItems.toArray(referencedItemsArray); - return DebugInfoItem.internDebugInfoItem(dexFile, lineStart, parameterNamesArray, out.toByteArray(), - referencedItemsArray); - } - - public static byte calculateSpecialOpcode(int lineDelta, int addressDelta) { - return (byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE)); - } - - private interface Event - { - int getAddress(); - void emit(DexFile dexFile, Output out, List referencedItems); - } - - private void emitEndSequence(Output out) { - out.writeByte(0); - } - - private void emitAdvancePC(Output out, int address) { - int addressDelta = address-currentAddress; - - if (addressDelta > 0) { - out.writeByte(1); - out.writeUnsignedLeb128(addressDelta); - currentAddress = address; - } - } - - private void emitAdvanceLine(Output out, int lineDelta) { - out.writeByte(2); - out.writeSignedLeb128(lineDelta); - } - - private void emitStartLocal(Output out, int registerNum) { - out.writeByte(3); - out.writeUnsignedLeb128(registerNum); - out.writeByte(1); - out.writeByte(1); - } - - private void emitStartLocalExtended(Output out, int registerNum) { - out.writeByte(4); - out.writeUnsignedLeb128(registerNum); - out.writeByte(1); - out.writeByte(1); - out.writeByte(1); - } - - private void emitEndLocal(Output out, int registerNum) { - out.writeByte(5); - out.writeUnsignedLeb128(registerNum); - } - - private void emitRestartLocal(Output out, int registerNum) { - out.writeByte(6); - out.writeUnsignedLeb128(registerNum); - } - - private void emitSetPrologueEnd(Output out) { - out.writeByte(7); - } - - private void emitSetEpilogueBegin(Output out) { - out.writeByte(8); - } - - private void emitSetFile(Output out) { - out.writeByte(9); - out.writeByte(1); - } - - private void emitSpecialOpcode(Output out, byte opcode) { - out.writeByte(opcode); - } - - private class LineEvent implements Event - { - private final int address; - private final int line; - - public LineEvent(int address, int line) { - this.address = address; - this.line = line; - } - - public int getAddress() { - return address; - } - - public void emit(DexFile dexFile, Output out, List referencedItems) { - int lineDelta = line - currentLine; - int addressDelta = address - currentAddress; - - if (lineDelta < -4 || lineDelta > 10) { - emitAdvanceLine(out, lineDelta); - lineDelta = 0; - } - if (lineDelta < 2 && addressDelta > 16 || lineDelta > 1 && addressDelta > 15) { - emitAdvancePC(out, address); - addressDelta = 0; - } - - //TODO: need to handle the case when the line delta is larger than a signed int - emitSpecialOpcode(out, calculateSpecialOpcode(lineDelta, addressDelta)); - - currentAddress = address; - currentLine = line; - } - } - - private class StartLocalEvent implements Event - { - private final int address; - private final int registerNum; - private final String localName; - private final String localType; - - public StartLocalEvent(int address, int registerNum, String localName, String localType) { - this.address = address; - this.registerNum = registerNum; - this.localName = localName; - this.localType = localType; - } - - public int getAddress() { - return address; - } - - public void emit(DexFile dexFile, Output out, List referencedItems) { - emitAdvancePC(out, address); - emitStartLocal(out, registerNum); - referencedItems.add(localName==null?null:StringIdItem.internStringIdItem(dexFile, localName)); - referencedItems.add(localType==null?null:TypeIdItem.internTypeIdItem(dexFile, - StringIdItem.internStringIdItem(dexFile, localType))); - } - } - - private class StartLocalExtendedEvent implements Event - { - private final int address; - private final int registerNum; - private final String localName; - private final String localType; - private final String signature; - - public StartLocalExtendedEvent(int address, int registerNum, String localName, String localType, - String signature) { - this.address = address; - this.registerNum = registerNum; - this.localName = localName; - this.localType = localType; - this.signature = signature; - } - - public int getAddress() { - return address; - } - - public void emit(DexFile dexFile, Output out, List referencedItems) { - emitAdvancePC(out, address); - emitStartLocalExtended(out, registerNum); - if (localName != null) { - referencedItems.add(StringIdItem.internStringIdItem(dexFile, localName)); - } - if (localType != null) { - referencedItems.add(TypeIdItem.internTypeIdItem(dexFile, - StringIdItem.internStringIdItem(dexFile, localType))); - } - if (signature != null) { - referencedItems.add(StringIdItem.internStringIdItem(dexFile, signature)); - } - } - } - - private class EndLocalEvent implements Event - { - private final int address; - private final int registerNum; - - public EndLocalEvent(int address, int registerNum) { - this.address = address; - this.registerNum = registerNum; - } - - public int getAddress() { - return address; - } - - public void emit(DexFile dexFile, Output out, List referencedItems) { - emitAdvancePC(out, address); - emitEndLocal(out, registerNum); - } - } - - private class RestartLocalEvent implements Event - { - private final int address; - private final int registerNum; - - public RestartLocalEvent(int address, int registerNum) { - this.address = address; - this.registerNum = registerNum; - } - - public int getAddress() { - return address; - } - - public void emit(DexFile dexFile, Output out, List referencedItems) { - emitAdvancePC(out, address); - emitRestartLocal(out, registerNum); - } - } - - private class PrologueEvent implements Event - { - private final int address; - - public PrologueEvent(int address) { - this.address = address; - } - - public int getAddress() { - return address; - } - - public void emit(DexFile dexFile, Output out, List referencedItems) { - emitAdvancePC(out, address); - emitSetPrologueEnd(out); - } - } - - private class EpilogueEvent implements Event - { - private final int address; - - public EpilogueEvent(int address) { - this.address = address; - } - - public int getAddress() { - return address; - } - - public void emit(DexFile dexFile, Output out, List referencedItems) { - emitAdvancePC(out, address); - emitSetEpilogueBegin(out); - } - } - - private class SetFileEvent implements Event - { - private final int address; - private final String fileName; - - public SetFileEvent(int address, String fileName) { - this.address = address; - this.fileName = fileName; - } - - public int getAddress() { - return address; - } - - public void emit(DexFile dexFile, Output out, List referencedItems) { - emitAdvancePC(out, address); - emitSetFile(out); - if (fileName != null) { - referencedItems.add(StringIdItem.internStringIdItem(dexFile, fileName)); - } - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java deleted file mode 100644 index 91f14072..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/EncodedValueUtils.java +++ /dev/null @@ -1,143 +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.Util; - -public class EncodedValueUtils { - public static byte getRequiredBytesForSignedIntegralValue(long value) { - /* - * Figure out how many bits are needed to represent the value, - * including a sign bit: The bit count is subtracted from 65 - * and not 64 to account for the sign bit. The xor operation - * has the effect of leaving non-negative values alone and - * unary complementing negative values (so that a leading zero - * count always returns a useful number for our present - * purpose). - */ - int requiredBits = - 65 - Long.numberOfLeadingZeros(value ^ (value >> 63)); - - // Round up the requiredBits to a number of bytes. - return (byte)((requiredBits + 0x07) >> 3); - } - - public static long decodeSignedIntegralValue(byte[] bytes) { - long value = 0; - for (int i = 0; i < bytes.length; i++) { - value |= (((long)(bytes[i] & 0xFF)) << (i * 8)); - } - - int shift = (8 - bytes.length) * 8; - return value << shift >> shift; - } - - public static byte[] encodeSignedIntegralValue(long value) { - int requiredBytes = getRequiredBytesForSignedIntegralValue(value); - - byte[] bytes = new byte[requiredBytes]; - - for (int i = 0; i < requiredBytes; i++) { - bytes[i] = (byte) value; - value >>= 8; - } - return bytes; - } - - - - - - public static byte getRequiredBytesForUnsignedIntegralValue(long value) { - // Figure out how many bits are needed to represent the value. - int requiredBits = 64 - Long.numberOfLeadingZeros(value); - if (requiredBits == 0) { - requiredBits = 1; - } - - // Round up the requiredBits to a number of bytes. - return (byte)((requiredBits + 0x07) >> 3); - } - - public static long decodeUnsignedIntegralValue(byte[] bytes) { - long value = 0; - for (int i = 0; i < bytes.length; i++) { - value |= (((long)(bytes[i] & 0xFF)) << i * 8); - } - return value; - } - - public static byte[] encodeUnsignedIntegralValue(long value) { - int requiredBytes = getRequiredBytesForUnsignedIntegralValue(value); - - byte[] bytes = new byte[requiredBytes]; - - for (int i = 0; i < requiredBytes; i++) { - bytes[i] = (byte) value; - value >>= 8; - } - return bytes; - } - - - - - - public static int getRequiredBytesForRightZeroExtendedValue(long value) { - // Figure out how many bits are needed to represent the value. - int requiredBits = 64 - Long.numberOfTrailingZeros(value); - if (requiredBits == 0) { - requiredBits = 1; - } - - // Round up the requiredBits to a number of bytes. - return (requiredBits + 0x07) >> 3; - } - - public static long decodeRightZeroExtendedValue(byte[] bytes) { - long value = 0; - for (int i = 0; i < bytes.length; i++) { - value |= (((long)(bytes[i] & 0xFF)) << (i * 8)); - } - return value << (8 - bytes.length) * 8; - } - - public static byte[] encodeRightZeroExtendedValue(long value) { - int requiredBytes = getRequiredBytesForRightZeroExtendedValue(value); - - // Scootch the first bits to be written down to the low-order bits. - value >>= 64 - (requiredBytes * 8); - - byte[] bytes = new byte[requiredBytes]; - - for(int i = 0; i < requiredBytes; i++) { - bytes[i] = (byte)value; - value >>= 8; - } - return bytes; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java deleted file mode 100644 index d8cd9bc8..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/FileUtils.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * As per the Apache license requirements, this file has been modified - * from its original state. - * - * Such modifications are Copyright (C) 2010 Ben Gruver, and are released - * under the original license - */ - -package org.jf.dexlib.Util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * File I/O utilities. - */ -public final class FileUtils { - /** - * This class is uninstantiable. - */ - private FileUtils() { - // This space intentionally left blank. - } - - /** - * Reads the named file, translating {@link IOException} to a - * {@link RuntimeException} of some sort. - * - * @param fileName non-null; name of the file to read - * @return non-null; contents of the file - */ - public static byte[] readFile(String fileName) - throws IOException { - File file = new File(fileName); - return readFile(file); - } - - /** - * Reads the given file, translating {@link IOException} to a - * {@link RuntimeException} of some sort. - * - * @param file non-null; the file to read - * @return non-null; contents of the file - */ - public static byte[] readFile(File file) - throws IOException { - return readFile(file, 0, -1); - } - - /** - * Reads the specified block from the given file, translating - * {@link IOException} to a {@link RuntimeException} of some sort. - * - * @param file non-null; the file to read - * @param offset the offset to begin reading - * @param length the number of bytes to read, or -1 to read to the - * end of the file - * @return non-null; contents of the file - */ - public static byte[] readFile(File file, int offset, int length) - throws IOException { - if (!file.exists()) { - throw new RuntimeException(file + ": file not found"); - } - - if (!file.isFile()) { - throw new RuntimeException(file + ": not a file"); - } - - if (!file.canRead()) { - throw new RuntimeException(file + ": file not readable"); - } - - long longLength = file.length(); - int fileLength = (int) longLength; - if (fileLength != longLength) { - throw new RuntimeException(file + ": file too long"); - } - - if (length == -1) { - length = fileLength - offset; - } - - if (offset + length > fileLength) { - throw new RuntimeException(file + ": file too short"); - } - - FileInputStream in = new FileInputStream(file); - - int at = offset; - while(at > 0) { - long amt = in.skip(at); - if (amt == -1) { - throw new RuntimeException(file + ": unexpected EOF"); - } - at -= amt; - } - - byte[] result = readStream(in, length); - - in.close(); - - return result; - } - - public static byte[] readStream(InputStream in, int length) - throws IOException { - byte[] result = new byte[length]; - int at=0; - - while (length > 0) { - int amt = in.read(result, at, length); - if (amt == -1) { - throw new RuntimeException("unexpected EOF"); - } - at += amt; - length -= amt; - } - - return result; - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Input.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Input.java deleted file mode 100644 index 2364fabf..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Input.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * As per the Apache license requirements, this file has been modified - * from its original state. - * - * Such modifications are Copyright (C) 2010 Ben Gruver, and are released - * under the original license - */ - -package org.jf.dexlib.Util; - -/** - * Interface for a source for binary input. This is similar to - * java.util.DataInput, but no IOExceptions - * are declared, and multibyte input is defined to be little-endian. - */ -public interface Input { - /** - * Gets the current cursor position. This is the same as the number of - * bytes read from this instance. - * - * @return >= 0; the cursor position - */ - public int getCursor(); - - /** - * Sets the current cursor position. - * - * @return >= 0; the cursor position - */ - public void setCursor(int cursor); - - /** - * Asserts that the cursor is the given value. - * - * @param expectedCursor the expected cursor value - * @throws RuntimeException thrown if getCursor() != - * expectedCursor - */ - public void assertCursor(int expectedCursor); - - /** - * Reads a byte from this instance. - * - * @return the byte value that was read - */ - public byte readByte(); - - /** - * Reads a short from this instance. - * - * @return the short value that was read, as an int - */ - public int readShort(); - - /** - * Reads an int from this instance. - * - * @return the unsigned int value that was read - */ - public int readInt(); - - /** - * Reads a long from this instance. - * - * @return the long value that was read - */ - public long readLong(); - - - /** - * Reads a DWARFv3-style signed LEB128 integer. For details, - * see the "Dalvik Executable Format" document or DWARF v3 section - * 7.6. - * - * @return the integer value that was read - */ - public int readSignedLeb128(); - - /** - * Reads a DWARFv3-style unsigned LEB128 integer. For details, - * see the "Dalvik Executable Format" document or DWARF v3 section - * 7.6. - * - * @return the integer value that was read - */ - public int readUnsignedLeb128(); - - - /** - * Reads a unsigned value as a DWARFv3-style LEB128 integer. It specifically - * checks for the case when the value was incorrectly formatted as a signed - * LEB128, and returns the appropriate unsigned value, but negated - * @return If the value was formatted as a ULEB128, it returns the actual unsigned - * value. Otherwise, if the value was formatted as a signed LEB128, it negates the - * "correct" unsigned value and returns that - */ - public int readUnsignedOrSignedLeb128(); - - /** - * reads a byte[] from this instance. - * - * @param bytes non-null; the buffer to read the data into - * @param offset >= 0; offset into bytes for the first - * byte to write - * @param length >= 0; number of bytes to read - */ - public void read(byte[] bytes, int offset, int length); - - /** - * reads a byte[] from this instance. This is just - * a convenient shorthand for read(bytes, 0, bytes.length). - * - * @param bytes non-null; the buffer to read the data into - */ - public void read(byte[] bytes); - - - /** - * reads a byte[] from this instance - * - * @param length >= 0; number of bytes to read - * @return a byte array containing length bytes - */ - public byte[] readBytes(int length); - - /** - * reads and decodes a null terminated utf8 string from the current cursor up to but not including - * the next null (0) byte. The terminating null byte is read and discarded, so that after the read, - * the cursor is positioned at the byte immediately after the terminating null - * - * @return a string representing the decoded value - */ - public String realNullTerminatedUtf8String(); - - /** - * Skips the given number of bytes. - * - * @param count >= 0; the number of bytes to skip - */ - public void skipBytes(int count); - - /** - * Skip extra bytes if necessary to force alignment of the output - * cursor as given. - * - * @param alignment > 0; the alignment; must be a power of two - */ - public void alignTo(int alignment); -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java deleted file mode 100644 index a5aafe68..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * As per the Apache license requirements, this file has been modified - * from its original state. - * - * Such modifications are Copyright (C) 2010 Ben Gruver, and are released - * under the original license - */ - -package org.jf.dexlib.Util; - -/** - * LEB128 (little-endian base 128) utilities. - */ -public final class Leb128Utils { - /** - * This class is uninstantiable. - */ - private Leb128Utils() { - // This space intentionally left blank. - } - - /** - * Gets the number of bytes in the unsigned LEB128 encoding of the - * given value. - * - * @param value the value in question - * @return its write size, in bytes - */ - public static int unsignedLeb128Size(int value) { - // TODO: This could be much cleverer. - - int remaining = value >>> 7; - int count = 0; - - while (remaining != 0) { - value = remaining; - remaining >>>= 7; - count++; - } - - return count + 1; - } - - /** - * Gets the number of bytes in the signed LEB128 encoding of the - * given value. - * - * @param value the value in question - * @return its write size, in bytes - */ - public static int signedLeb128Size(int value) { - // TODO: This could be much cleverer. - - int remaining = value >> 7; - int count = 0; - boolean hasMore = true; - int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; - - while (hasMore) { - hasMore = (remaining != end) - || ((remaining & 1) != ((value >> 6) & 1)); - - value = remaining; - remaining >>= 7; - count++; - } - - return count; - } - - /** - * Writes an unsigned leb128 to the buffer at the specified location - * @param value the value to write as an unsigned leb128 - * @param buffer the buffer to write to - * @param bufferIndex the index to start writing at - */ - public static void writeUnsignedLeb128(int value, byte[] buffer, int bufferIndex) { - int remaining = value >>> 7; - int count = 0; - - while (remaining != 0) { - buffer[bufferIndex] = (byte)((value & 0x7f) | 0x80); - bufferIndex++; - value = remaining; - remaining >>>= 7; - count++; - } - - buffer[bufferIndex] = (byte)(value & 0x7f); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java deleted file mode 100644 index 2e97f512..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/NumberUtils.java +++ /dev/null @@ -1,200 +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.Util; - -public class NumberUtils { - - /** - * Decodes the high signed 4-bit nibble from the given byte - * @param b the byte to decode - * @return the decoded signed nibble - */ - public static byte decodeHighSignedNibble(byte b) { - return (byte)(b >> 4); - } - - /** - * Decodes the low signed 4-bit nibble from the given byte - * @param b the byte to decode - * @return the decoded signed nibble - */ - public static byte decodeLowSignedNibble(byte b) { - return (byte)(((byte)(b << 4)) >> 4); - } - - /** - * Decodes the high unsigned 4-bit nibble from the given byte - * @param b the byte to decode - * @return the decoded unsigned nibble - */ - public static byte decodeHighUnsignedNibble(byte b) { - return (byte)((b & 0xFF) >>> 4); - } - - /** - * Decodes the low unsigned 4-bit nibble from the given byte - * @param b the byte to decode - * @return the decoded unsigned nibble - */ - public static byte decodeLowUnsignedNibble(byte b) { - return (byte)(b & 0x0F); - } - - /** - * Decodes an unsigned byte from a signed byte - * @param b the signed byte to decode - * @return the decoded unsigned byte as a short - */ - public static short decodeUnsignedByte(byte b) { - return (short)(b & 0xFF); - } - - /** - * Decodes a signed short value from 2 individual bytes - * The parameters are in order from least significant byte to most significant byte - * @param lsb the least significant byte - * @param msb the most significant byte - * @return the decoded signed short value - */ - public static short decodeShort(byte lsb, byte msb) { - return (short) - ( (lsb & 0xFF) | - (msb << 8) - ); - } - - /** - * Decodes a signed short value in little endian format from the given byte array at the given index. - * @param bytes the byte array - * @param index the index of the first byte of the signed short value to decode - * @return the decoded signed short value - */ - public static short decodeShort(byte[] bytes, int index) { - return (short) - ( (bytes[index++] & 0xFF) | - (bytes[index] << 8) - ); - } - - /** - * Decodes an unsigned short value from 2 individual bytes - * The parameters are in order from least significant byte to most significant byte - * @param lsb the least significant byte - * @param msb the most significant byte - * @return the decoded unsigned short value as an int - */ - public static int decodeUnsignedShort(byte lsb, byte msb) { - return ( (lsb & 0xFF) | - ((msb & 0xFF) << 8) - ); - } - - /** - * Decodes an unsigned short value in little endian format from the given byte array at the given index. - * @param bytes the byte array - * @param index the index of the first byte of the unsigned short value to decode - * @return the decoded unsigned short value as an int - */ - public static int decodeUnsignedShort(byte[] bytes, int index) { - return ( (bytes[index++] & 0xFF) | - ((bytes[index] & 0xFF) << 8) - ); - } - - /** - * Decodes a signed integer value from 4 individual bytes - * The parameters are in order from least significant byte to most significant byte - * @param lsb the least significant byte - * @param mlsb the middle least significant byte - * @param mmsb the middle most significant byte - * @param msb the most significant byte - * @return the decoded signed integer value - */ - public static int decodeInt(byte lsb, byte mlsb, byte mmsb, byte msb) { - return (lsb & 0xFF) | - ((mlsb & 0xFF) << 8) | - ((mmsb & 0xFF) << 16) | - (msb << 24); - } - - /** - * Decodes a signed integer value in little endian format from the given byte array at the given index. - * @param bytes the byte array - * @param index the index of the first byte of the signed integer value to decode - * @return the decoded signed integer value - */ - public static int decodeInt(byte[] bytes, int index) { - return (bytes[index++] & 0xFF) | - ((bytes[index++] & 0xFF) << 8) | - ((bytes[index++] & 0xFF) << 16) | - (bytes[index] << 24); - } - - /** - * Decodes a signed long value from 8 individual bytes - * The parameters are in order from least significant byte to most significant byte - * @param llsb the lower least significant byte - * @param lmlsb the lower middle least significant byte - * @param lmmsb the lower middle most significant byte - * @param lgsb the lower greater significant byte - * @param glsb the greater least significant byte - * @param gmlsb the greater middle least significant byte - * @param gmmsb the greater middle most significant byte - * @param gmsb the greater most significant byte - * @return the decoded signed long value - */ - public static long decodeLong(byte llsb, byte lmlsb, byte lmmsb, byte lgsb, byte glsb, byte gmlsb, byte gmmsb, - byte gmsb) { - return (llsb & 0xFFL) | - ((lmlsb & 0xFFL) << 8) | - ((lmmsb & 0xFFL) << 16) | - ((lgsb & 0xFFL) << 24) | - ((glsb & 0xFFL) << 32) | - ((gmlsb & 0xFFL) << 40) | - ((gmmsb & 0xFFL) << 48) | - (((long)gmsb) << 56); - } - - /** - * Decodes a signed long value in little endian format from the given byte array at the given index. - * @param bytes the byte array - * @param index the index of the first byte of the signed long value to decode - * @return the decoded signed long value - */ - public static long decodeLong(byte[] bytes, int index) { - return (bytes[index++] & 0xFFL) | - ((bytes[index++] & 0xFFL) << 8) | - ((bytes[index++] & 0xFFL) << 16) | - ((bytes[index++] & 0xFFL) << 24) | - ((bytes[index++] & 0xFFL) << 32) | - ((bytes[index++] & 0xFFL) << 40) | - ((bytes[index++] & 0xFFL) << 48) | - (((long)bytes[index]) << 56); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Output.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Output.java deleted file mode 100644 index 49b41336..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Output.java +++ /dev/null @@ -1,141 +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.Util; - -/** - * Interface for a sink for binary output. This is similar to - * java.util.DataOutput, but no IOExceptions - * are declared, and multibyte output is defined to be little-endian. - */ -public interface Output { - /** - * Gets the current cursor position. This is the same as the number of - * bytes written to this instance. - * - * @return >= 0; the cursor position - */ - public int getCursor(); - - /** - * Asserts that the cursor is the given value. - * - * @param expectedCursor the expected cursor value - * @throws RuntimeException thrown if getCursor() != - * expectedCursor - */ - public void assertCursor(int expectedCursor); - - /** - * Writes a byte to this instance. - * - * @param value the value to write; all but the low 8 bits are ignored - */ - public void writeByte(int value); - - /** - * Writes a short to this instance. - * - * @param value the value to write; all but the low 16 bits are ignored - */ - public void writeShort(int value); - - /** - * Writes an int to this instance. - * - * @param value the value to write - */ - public void writeInt(int value); - - /** - * Writes a long to this instance. - * - * @param value the value to write - */ - public void writeLong(long value); - - /** - * Writes a DWARFv3-style unsigned LEB128 integer. For details, - * see the "Dalvik Executable Format" document or DWARF v3 section - * 7.6. - * - * @param value value to write, treated as an unsigned value - * @return 1..5; the number of bytes actually written - */ - public int writeUnsignedLeb128(int value); - - /** - * Writes a DWARFv3-style unsigned LEB128 integer. For details, - * see the "Dalvik Executable Format" document or DWARF v3 section - * 7.6. - * - * @param value value to write - * @return 1..5; the number of bytes actually written - */ - public int writeSignedLeb128(int value); - - /** - * Writes a {@link org.jf.dexlib.Util.ByteArray} to this instance. - * - * @param bytes non-null; the array to write - */ - public void write(ByteArray bytes); - - /** - * Writes a portion of a byte[] to this instance. - * - * @param bytes non-null; the array to write - * @param offset >= 0; offset into bytes for the first - * byte to write - * @param length >= 0; number of bytes to write - */ - public void write(byte[] bytes, int offset, int length); - - /** - * Writes a byte[] to this instance. This is just - * a convenient shorthand for write(bytes, 0, bytes.length). - * - * @param bytes non-null; the array to write - */ - public void write(byte[] bytes); - - /** - * Writes the given number of 0 bytes. - * - * @param count >= 0; the number of zeroes to write - */ - public void writeZeroes(int count); - - /** - * Adds extra bytes if necessary (with value 0) to - * force alignment of the output cursor as given. - * - * @param alignment > 0; the alignment; must be a power of two - */ - public void alignTo(int alignment); -} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java deleted file mode 100644 index f246c999..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/Pair.java +++ /dev/null @@ -1,40 +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.Util; - -public class Pair { - public final A first; - public final B second; - - public Pair(A first, B second) { - this.first = first; - this.second = second; - } -} - diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java deleted file mode 100644 index 2667979d..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/ReadOnlyArrayList.java +++ /dev/null @@ -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.Util; - -import java.util.AbstractList; -import java.util.RandomAccess; - -public class ReadOnlyArrayList extends AbstractList implements RandomAccess { - private final T[] arr; - - public ReadOnlyArrayList(T[] arr) { - this.arr = arr; - } - - public int size() { - return arr.length; - } - - public T get(int i) { - return arr[i]; - } - - public static ReadOnlyArrayList of(T... items) { - return new ReadOnlyArrayList(items); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java deleted file mode 100644 index aadcaa7a..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TryListBuilder.java +++ /dev/null @@ -1,347 +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.Util; - -import org.jf.dexlib.CodeItem; -import org.jf.dexlib.TypeIdItem; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; - -public class TryListBuilder -{ - /*TODO: add logic to merge adjacent, identical try blocks, and remove superflous handlers - Also provide a "strict" mode, where the above isn't performed, which will be useful to be able to - exactly reproduce the original .dex file (for testing/verification purposes)*/ - - - private TryRange firstTryRange = new TryRange(0,0); - private TryRange lastTryRange = new TryRange(0,0); - - public TryListBuilder() { - firstTryRange.next = lastTryRange; - lastTryRange.previous = firstTryRange; - } - - private class TryRange - { - public TryRange previous = null; - public TryRange next = null; - - public int startAddress; - public int endAddress; - public LinkedList handlers; - - public int catchAllHandlerAddress; - - public TryRange(int startAddress, int endAddress) { - this.startAddress = startAddress; - this.endAddress = endAddress; - this.handlers = new LinkedList(); - this.previous = null; - this.next = null; - catchAllHandlerAddress = -1; - } - - public void append(TryRange tryRange) { - /*we use a dummy last item, so this.next will always - have a value*/ - this.next.previous = tryRange; - tryRange.next = this.next; - - this.next = tryRange; - tryRange.previous = this; - } - - public void prepend(TryRange tryRange){ - /*we use a dummy first item, so this.previous will always - have a value*/ - this.previous.next = tryRange; - tryRange.previous = this.previous; - - this.previous = tryRange; - tryRange.next = this; - } - - /** - * This splits the current range into two ranges at the given - * address. The existing range will be shortened to the first - * half, and a new range will be created and returned for the - * 2nd half. - * @param address The address to split at - * @return The 2nd half of the - */ - public TryRange split(int address) { - //this is a private class, so address is assumed - //to be valid - - TryRange tryRange = new TryRange(address, endAddress); - tryRange.catchAllHandlerAddress = this.catchAllHandlerAddress; - tryRange.handlers.addAll(this.handlers); - append(tryRange); - - this.endAddress = address; - - return tryRange; - } - - public void appendHandler(Handler handler) { - handlers.addLast(handler); - } - - public void prependHandler(Handler handler) { - handlers.addFirst(handler); - } - } - - private class Handler - { - public final TypeIdItem type; - public final int handlerAddress; - - public Handler(TypeIdItem type, int handlerAddress) { - this.type = type; - this.handlerAddress = handlerAddress; - } - } - - public Pair, List> encodeTries() { - if (firstTryRange.next == lastTryRange) { - return new Pair, List>(null, null); - } - - ArrayList tries = new ArrayList(); - ArrayList handlers = new ArrayList(); - - HashMap handlerDict = - new HashMap(); - - TryRange tryRange = firstTryRange.next; - - while (tryRange != lastTryRange) { - CodeItem.EncodedTypeAddrPair[] encodedTypeAddrPairs = - new CodeItem.EncodedTypeAddrPair[tryRange.handlers.size()]; - - int index = 0; - for (Handler handler: tryRange.handlers) { - CodeItem.EncodedTypeAddrPair encodedTypeAddrPair = new CodeItem.EncodedTypeAddrPair( - handler.type, - handler.handlerAddress); - encodedTypeAddrPairs[index++] = encodedTypeAddrPair; - } - - CodeItem.EncodedCatchHandler encodedCatchHandler = new CodeItem.EncodedCatchHandler( - encodedTypeAddrPairs, - tryRange.catchAllHandlerAddress); - CodeItem.EncodedCatchHandler internedEncodedCatchHandler = handlerDict.get(encodedCatchHandler); - if (internedEncodedCatchHandler == null) { - handlerDict.put(encodedCatchHandler, encodedCatchHandler); - handlers.add(encodedCatchHandler); - } else { - encodedCatchHandler = internedEncodedCatchHandler; - } - - CodeItem.TryItem tryItem = new CodeItem.TryItem( - tryRange.startAddress, - tryRange.endAddress - tryRange.startAddress, - encodedCatchHandler); - tries.add(tryItem); - - tryRange = tryRange.next; - } - - return new Pair, List>(tries, handlers); - } - - public void addCatchAllHandler(int startAddress, int endAddress, int handlerAddress) { - TryRange startRange; - TryRange endRange; - - Pair ranges = getBoundingRanges(startAddress, endAddress); - startRange = ranges.first; - endRange = ranges.second; - - int previousEnd = startAddress; - TryRange tryRange = startRange; - - /*Now we have the start and end ranges that exactly match the start and end - of the range being added. We need to iterate over all the ranges from the start - to end range inclusively, and append the handler to the end of each range's handler - list. We also need to create a new range for any "holes" in the existing ranges*/ - do - { - //is there a hole? If so, add a new range to fill the hole - if (tryRange.startAddress > previousEnd) { - TryRange newRange = new TryRange(previousEnd, tryRange.startAddress); - tryRange.prepend(newRange); - tryRange = newRange; - } - - if (tryRange.catchAllHandlerAddress == -1) { - tryRange.catchAllHandlerAddress = handlerAddress; - } - - previousEnd = tryRange.endAddress; - tryRange = tryRange.next; - } while (tryRange.previous != endRange); - } - - public Pair getBoundingRanges(int startAddress, int endAddress) { - TryRange startRange = null; - TryRange endRange = null; - - TryRange tryRange = firstTryRange.next; - while (tryRange != lastTryRange) { - if (startAddress == tryRange.startAddress) { - //|-----| - //^------ - /*Bam. We hit the start of the range right on the head*/ - startRange = tryRange; - break; - } else if (startAddress > tryRange.startAddress && startAddress < tryRange.endAddress) { - //|-----| - // ^---- - /*Almost. The start of the range being added is in the middle - of an existing try range. We need to split the existing range - at the start address of the range being added*/ - startRange = tryRange.split(startAddress); - break; - }else if (startAddress < tryRange.startAddress) { - if (endAddress <= tryRange.startAddress) { - // |-----| - //^--^ - /*Oops, totally too far! The new range doesn't overlap any existing - ones, so we just add it and return*/ - startRange = new TryRange(startAddress, endAddress); - tryRange.prepend(startRange); - return new Pair(startRange, startRange); - } else { - // |-----| - //^--------- - /*Oops, too far! We've passed the start of the range being added, but - the new range does overlap this one. We need to add a new range just - before this one*/ - startRange = new TryRange(startAddress, tryRange.startAddress); - tryRange.prepend(startRange); - break; - } - } - - tryRange = tryRange.next; - } - - //|-----| - // ^----- - /*Either the list of tries is blank, or all the tries in the list - end before the range being added starts. In either case, we just need - to add a new range at the end of the list*/ - if (startRange == null) { - startRange = new TryRange(startAddress, endAddress); - lastTryRange.prepend(startRange); - return new Pair(startRange, startRange); - } - - tryRange = startRange; - while (tryRange != lastTryRange) { - if (tryRange.endAddress == endAddress) { - //|-----| - //------^ - /*Bam! We hit the end right on the head.*/ - endRange = tryRange; - break; - } else if (tryRange.startAddress < endAddress && tryRange.endAddress > endAddress) { - //|-----| - //--^ - /*Almost. The range being added ends in the middle of an - existing range. We need to split the existing range - at the end of the range being added.*/ - tryRange.split(endAddress); - endRange = tryRange; - break; - } else if (tryRange.startAddress >= endAddress) { - //|-----| |-----| - //-----------^ - /*Oops, too far! The current range starts after the range being added - ends. We need to create a new range that starts at the end of the - previous range, and ends at the end of the range being added*/ - endRange = new TryRange(tryRange.previous.endAddress, endAddress); - tryRange.prepend(endRange); - break; - } - tryRange = tryRange.next; - } - - //|-----| - //--------^ - /*The last range in the list ended before the end of the range being added. - We need to add a new range that starts at the end of the last range in the - list, and ends at the end of the range being added.*/ - if (endRange == null) { - endRange = new TryRange(lastTryRange.previous.endAddress, endAddress); - lastTryRange.prepend(endRange); - } - - return new Pair(startRange, endRange); - } - - public void addHandler(TypeIdItem type, int startAddress, int endAddress, int handlerAddress) { - TryRange startRange; - TryRange endRange; - - //TODO: need to check for pre-existing exception types in the handler list? - - Pair ranges = getBoundingRanges(startAddress, endAddress); - startRange = ranges.first; - endRange = ranges.second; - Handler handler = new Handler(type, handlerAddress); - - int previousEnd = startAddress; - TryRange tryRange = startRange; - - /*Now we have the start and end ranges that exactly match the start and end - of the range being added. We need to iterate over all the ranges from the start - to end range inclusively, and append the handler to the end of each range's handler - list. We also need to create a new range for any "holes" in the existing ranges*/ - do - { - //is there a hole? If so, add a new range to fill the hole - if (tryRange.startAddress > previousEnd) { - TryRange newRange = new TryRange(previousEnd, tryRange.startAddress); - tryRange.prepend(newRange); - tryRange = newRange; - } - - tryRange.appendHandler(handler); - previousEnd = tryRange.endAddress; - tryRange = tryRange.next; - } while (tryRange.previous != endRange); - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java deleted file mode 100644 index d064a4da..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TwoColumnOutput.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * As per the Apache license requirements, this file has been modified - * from its original state. - * - * Such modifications are Copyright (C) 2010 Ben Gruver, and are released - * under the original license - */ - -package org.jf.dexlib.Util; - -import java.io.*; - -/** - * Class that takes a combined output destination and provides two - * output writers, one of which ends up writing to the left column and - * one which goes on the right. - */ -public final class TwoColumnOutput { - /** non-null; underlying writer for final output */ - private final Writer out; - - /** > 0; the left column width */ - private final int leftWidth; - - /** non-null; pending left column output */ - private final StringBuffer leftBuf; - - /** non-null; pending right column output */ - private final StringBuffer rightBuf; - - /** non-null; left column writer */ - private final IndentingWriter leftColumn; - - /** non-null; right column writer */ - private final IndentingWriter rightColumn; - - /** - * Turns the given two strings (with widths) and spacer into a formatted - * two-column string. - * - * @param s1 non-null; first string - * @param width1 > 0; width of the first column - * @param spacer non-null; spacer string - * @param s2 non-null; second string - * @param width2 > 0; width of the second column - * @return non-null; an appropriately-formatted string - */ - public static String toString(String s1, int width1, String spacer, - String s2, int width2) { - int len1 = s1.length(); - int len2 = s2.length(); - - StringWriter sw = new StringWriter((len1 + len2) * 3); - TwoColumnOutput twoOut = - new TwoColumnOutput(sw, width1, width2, spacer); - - try { - twoOut.getLeft().write(s1); - twoOut.getRight().write(s2); - } catch (IOException ex) { - throw new RuntimeException("shouldn't happen", ex); - } - - twoOut.flush(); - return sw.toString(); - } - - /** - * Constructs an instance. - * - * @param out non-null; writer to send final output to - * @param leftWidth > 0; width of the left column, in characters - * @param rightWidth > 0; width of the right column, in characters - * @param spacer non-null; spacer string to sit between the two columns - */ - public TwoColumnOutput(Writer out, int leftWidth, int rightWidth, - String spacer) { - if (out == null) { - throw new NullPointerException("out == null"); - } - - if (leftWidth < 1) { - throw new IllegalArgumentException("leftWidth < 1"); - } - - if (rightWidth < 1) { - throw new IllegalArgumentException("rightWidth < 1"); - } - - if (spacer == null) { - throw new NullPointerException("spacer == null"); - } - - StringWriter leftWriter = new StringWriter(1000); - StringWriter rightWriter = new StringWriter(1000); - - this.out = out; - this.leftWidth = leftWidth; - this.leftBuf = leftWriter.getBuffer(); - this.rightBuf = rightWriter.getBuffer(); - this.leftColumn = new IndentingWriter(leftWriter, leftWidth); - this.rightColumn = - new IndentingWriter(rightWriter, rightWidth, spacer); - } - - /** - * Constructs an instance. - * - * @param out non-null; stream to send final output to - * @param leftWidth >= 1; width of the left column, in characters - * @param rightWidth >= 1; width of the right column, in characters - * @param spacer non-null; spacer string to sit between the two columns - */ - public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth, - String spacer) { - this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer); - } - - /** - * Gets the writer to use to write to the left column. - * - * @return non-null; the left column writer - */ - public Writer getLeft() { - return leftColumn; - } - - /** - * Gets the writer to use to write to the right column. - * - * @return non-null; the right column writer - */ - public Writer getRight() { - return rightColumn; - } - - /** - * Flushes the output. If there are more lines of pending output in one - * column, then the other column will get filled with blank lines. - */ - public void flush() { - try { - appendNewlineIfNecessary(leftBuf, leftColumn); - appendNewlineIfNecessary(rightBuf, rightColumn); - outputFullLines(); - flushLeft(); - flushRight(); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - /** - * Outputs to the final destination as many full line pairs as - * there are in the pending output, removing those lines from - * their respective buffers. This method terminates when at - * least one of the two column buffers is empty. - */ - private void outputFullLines() throws IOException { - for (;;) { - int leftLen = leftBuf.indexOf("\n"); - if (leftLen < 0) { - return; - } - - int rightLen = rightBuf.indexOf("\n"); - if (rightLen < 0) { - return; - } - - if (leftLen != 0) { - out.write(leftBuf.substring(0, leftLen)); - } - - if (rightLen != 0) { - writeSpaces(out, leftWidth - leftLen); - out.write(rightBuf.substring(0, rightLen)); - } - - out.write('\n'); - - leftBuf.delete(0, leftLen + 1); - rightBuf.delete(0, rightLen + 1); - } - } - - /** - * Flushes the left column buffer, printing it and clearing the buffer. - * If the buffer is already empty, this does nothing. - */ - private void flushLeft() throws IOException { - appendNewlineIfNecessary(leftBuf, leftColumn); - - while (leftBuf.length() != 0) { - rightColumn.write('\n'); - outputFullLines(); - } - } - - /** - * Flushes the right column buffer, printing it and clearing the buffer. - * If the buffer is already empty, this does nothing. - */ - private void flushRight() throws IOException { - appendNewlineIfNecessary(rightBuf, rightColumn); - - while (rightBuf.length() != 0) { - leftColumn.write('\n'); - outputFullLines(); - } - } - - /** - * Appends a newline to the given buffer via the given writer, but - * only if it isn't empty and doesn't already end with one. - * - * @param buf non-null; the buffer in question - * @param out non-null; the writer to use - */ - private static void appendNewlineIfNecessary(StringBuffer buf, - Writer out) - throws IOException { - int len = buf.length(); - - if ((len != 0) && (buf.charAt(len - 1) != '\n')) { - out.write('\n'); - } - } - - /** - * Writes the given number of spaces to the given writer. - * - * @param out non-null; where to write - * @param amt >= 0; the number of spaces to write - */ - private static void writeSpaces(Writer out, int amt) throws IOException { - while (amt > 0) { - out.write(' '); - amt--; - } - } -} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java b/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java deleted file mode 100644 index 40cedda0..00000000 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/TypeUtils.java +++ /dev/null @@ -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.Util; - -import org.jf.dexlib.EncodedValue.*; -import org.jf.dexlib.TypeIdItem; - -public class TypeUtils -{ - public static EncodedValue makeDefaultValueForType(String type) { - switch (type.charAt(0)) { - case 'Z': - return BooleanEncodedValue.FalseValue; - case 'B': - return new ByteEncodedValue((byte)0); - case 'S': - return new ShortEncodedValue((short)0); - case 'C': - return new CharEncodedValue((char)0); - case 'I': - return new IntEncodedValue(0); - case 'J': - return new LongEncodedValue(0); - case 'F': - return new FloatEncodedValue(0); - case 'D': - return new DoubleEncodedValue(0); - case 'L': - case '[': - return NullEncodedValue.NullValue; - } - return null; - } - - public static EncodedValue makeDefaultValueForType(TypeIdItem type) { - return makeDefaultValueForType(type.getTypeDescriptor()); - } -} diff --git a/brut.apktool.smali/dexlib2/accessorTestGenerator/build.gradle b/brut.apktool.smali/dexlib2/accessorTestGenerator/build.gradle new file mode 100644 index 00000000..e242ae0e --- /dev/null +++ b/brut.apktool.smali/dexlib2/accessorTestGenerator/build.gradle @@ -0,0 +1,39 @@ +/* + * 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. + */ + +dependencies { + compile project(':brut.apktool.smali:util') + compile 'com.google.code.findbugs:jsr305:1.3.9' + compile 'com.google.guava:guava:13.0.1' + compile 'org.antlr:ST4:4.0.7' + + testCompile 'junit:junit:4.6' +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib2/accessorTestGenerator/src/main/java/org/jf/dexlib2/AccessorTestGenerator.java b/brut.apktool.smali/dexlib2/accessorTestGenerator/src/main/java/org/jf/dexlib2/AccessorTestGenerator.java new file mode 100644 index 00000000..6540b3df --- /dev/null +++ b/brut.apktool.smali/dexlib2/accessorTestGenerator/src/main/java/org/jf/dexlib2/AccessorTestGenerator.java @@ -0,0 +1,141 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +import org.stringtemplate.v4.*; + +import java.io.*; +import java.net.URL; + +public class AccessorTestGenerator { + private static class UnaryOperation { + public final String name; + public UnaryOperation(String name) { + this.name = name; + } + } + + private static class BinaryOperation { + public final String name; + public final String[] inputTypes; + public BinaryOperation(String name, String[] inputTypes) { + this.name = name; + this.inputTypes = inputTypes; + } + } + + private static class TypeDef { + public final String name; + public final UnaryOperation[] unaryOperations; + public final BinaryOperation[] binaryOperations; + public TypeDef(String name, UnaryOperation[] unaryOperations, BinaryOperation[] binaryOperations) { + this.name = name; + this.unaryOperations = unaryOperations; + this.binaryOperations = binaryOperations; + } + } + + private static final UnaryOperation[] unaryOperations = new UnaryOperation[] { + new UnaryOperation("preinc"), + new UnaryOperation("postinc"), + new UnaryOperation("predec"), + new UnaryOperation("postdec") + }; + + private static final String[] booleanInputs = new String[] {"boolean"}; + private static final String[] integralInputs = new String[] {"int", "long"}; + private static final String[] allInputs = new String[] {"int", "float", "long", "double"}; + + private static final BinaryOperation[] booleanOperations = new BinaryOperation[] { + new BinaryOperation("and", booleanInputs), + new BinaryOperation("or", booleanInputs), + new BinaryOperation("xor", booleanInputs), + }; + + private static final BinaryOperation[] floatOperations = new BinaryOperation[] { + new BinaryOperation("add", allInputs), + new BinaryOperation("sub", allInputs), + new BinaryOperation("mul", allInputs), + new BinaryOperation("div", allInputs), + new BinaryOperation("rem", allInputs), + }; + + private static final BinaryOperation[] integralOperations = new BinaryOperation[] { + new BinaryOperation("add", allInputs), + new BinaryOperation("sub", allInputs), + new BinaryOperation("mul", allInputs), + new BinaryOperation("div", allInputs), + new BinaryOperation("rem", allInputs), + new BinaryOperation("and", integralInputs), + new BinaryOperation("or", integralInputs), + new BinaryOperation("xor", integralInputs), + new BinaryOperation("shl", integralInputs), + new BinaryOperation("shr", integralInputs), + new BinaryOperation("ushr", integralInputs), + }; + + private static final TypeDef[] types = new TypeDef[] { + new TypeDef("boolean", new UnaryOperation[0], booleanOperations), + new TypeDef("byte", unaryOperations, integralOperations), + new TypeDef("char", unaryOperations, integralOperations), + new TypeDef("short", unaryOperations, integralOperations), + new TypeDef("int", unaryOperations, integralOperations), + new TypeDef("long", unaryOperations, integralOperations), + new TypeDef("float", unaryOperations, floatOperations), + new TypeDef("double", unaryOperations, floatOperations), + }; + + + public static void main(String[] args) throws IOException { + if (args.length != 1) { + System.err.println("Usage: java org.jf.dexlib2.AccessorTestGenerator "); + } + + URL stgUrl = AccessorTestGenerator.class.getClassLoader().getResource("AccessorTest.stg"); + STGroupFile stg = new STGroupFile(stgUrl, "utf-8", '<', '>'); + ST fileSt = stg.getInstanceOf("file"); + fileSt.add("types", types); + + PrintWriter w = null; + try { + w = new PrintWriter(new BufferedWriter(new FileWriter(args[0]))); + w.print(fileSt.render()); + } finally { + if (w != null) { + w.close(); + } + } + } +} + + + diff --git a/brut.apktool.smali/dexlib2/accessorTestGenerator/src/main/resources/AccessorTest.stg b/brut.apktool.smali/dexlib2/accessorTestGenerator/src/main/resources/AccessorTest.stg new file mode 100644 index 00000000..5c76eecd --- /dev/null +++ b/brut.apktool.smali/dexlib2/accessorTestGenerator/src/main/resources/AccessorTest.stg @@ -0,0 +1,86 @@ +decl(type, name, value) ::= " ;" +init(v) ::= " = " + +field_decl(type) ::= "private _val;" + +preinc_template(type) ::= "++_val;" +postinc_template(type) ::= "_val++;" +predec_template(type) ::= "--_val;" +postdec_template(type) ::= "_val--;" +add_template(type) ::= "_val += val;" +sub_template(type) ::= "_val -= val;" +mul_template(type) ::= "_val *= val;" +div_template(type) ::= "_val /= val;" +rem_template(type) ::= "_val %= val;" +and_template(type) ::= "_val &= val;" +or_template(type) ::= "_val |= val;" +xor_template(type) ::= "_val ^= val;" +shl_template(type) ::= "_val \<\<= val;" +shr_template(type) ::= "_val >>= val;" +ushr_template(type) ::= "_val >>>= val;" + +operation_template_name(operation) ::= "_template" + +binary_method(input, type, binary_operation) ::= << +public void _( val) { + <(operation_template_name(binary_operation))(type)> +} +>> + +binary_methods(binary_operation, type) ::= << + +>> + +unary_method(unary_operation, type) ::= << +public void _() { + <(operation_template_name(unary_operation))(type)> +} +>> + +type_methods(type) ::= << +<[type.unaryOperations:unary_method(type), type.binaryOperations:binary_methods(type)];separator="\n\n"> +>> + + +file(types) ::= << +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +public class AccessorTypes { + + + private class Accessors { + + } +} +>> diff --git a/brut.apktool.smali/dexlib2/build.gradle b/brut.apktool.smali/dexlib2/build.gradle new file mode 100644 index 00000000..e956b7d7 --- /dev/null +++ b/brut.apktool.smali/dexlib2/build.gradle @@ -0,0 +1,119 @@ +/* + * 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. + */ + +configurations { + accessorTestGenerator +} + +dependencies { + compile project(':brut.apktool.smali:util') + compile depends.findbugs + compile depends.guava + + testCompile depends.junit + + accessorTestGenerator project('accessorTestGenerator') +} + +ext.testAccessorOutputDir = file("${buildDir}/generated-accessor-test-sources") +ext.testAccessorOutputFile = file("${buildDir}/generated-accessor-test-sources/org/jf/dexlib2/AccessorTypes.java") + +sourceSets { + // The sources for building the test dex file for the accessor test + accessorTestDex { + java { + srcDir testAccessorOutputDir + } + } + + // The sources for the accessor test itself + accessorTest { + java { + compileClasspath += main.output + runtimeClasspath += main.output + } + } +} + +configurations { + accessorTestDexCompile.extendsFrom compile + accessorTestDexRuntime.extendsFrom runtime + + accessorTestCompile.extendsFrom testCompile + accessorTestRuntime.extendsFrom testRuntime +} + +idea { + module { + testSourceDirs += sourceSets.accessorTest.java.srcDirs + } +} + +// You must manually execute this task to regenerate SyntheticAccessorFSM.java, after modifying the ragel file +// e.g. ./gradlew ragel +task ragel(type:Exec) { + workingDir = 'src/main/ragel' + + commandLine 'ragel', '-J', '-o', file('src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java'), + 'SyntheticAccessorFSM.rl' +} + +task generateAccessorTestSource(type: JavaExec) { + outputs.dir file(testAccessorOutputDir) + + mkdir(file(testAccessorOutputFile).parent) + + classpath = configurations.accessorTestGenerator + main = 'org.jf.dexlib2.AccessorTestGenerator' + args testAccessorOutputFile +} +compileAccessorTestDexJava.dependsOn(generateAccessorTestSource) + +task generateAccessorTestDex(type: Exec, dependsOn: compileAccessorTestDexJava) { + def outputDex = file("${sourceSets.accessorTest.output.resourcesDir}/accessorTest.dex") + mkdir(outputDex.parent) + + inputs.dir project.sourceSets.accessorTestDex.output.classesDir + outputs.file outputDex + + sourceSets.accessorTest.resources + + workingDir project.sourceSets.accessorTestDex.output.classesDir + executable 'dx' + args '--dex' + args "--output=${outputDex}" + args '.' +} + +task accessorTest(type: Test, dependsOn: generateAccessorTestDex) { + testClassesDir = project.sourceSets.accessorTest.output.classesDir + classpath = project.sourceSets.accessorTest.runtimeClasspath +} diff --git a/brut.apktool.smali/dexlib2/src/accessorTest/java/org/jf/dexlib2/AccessorTest.java b/brut.apktool.smali/dexlib2/src/accessorTest/java/org/jf/dexlib2/AccessorTest.java new file mode 100644 index 00000000..13e7b30f --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/accessorTest/java/org/jf/dexlib2/AccessorTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +import com.google.common.collect.ImmutableMap; +import junit.framework.Assert; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.DexFile; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.ReferenceInstruction; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.util.SyntheticAccessorResolver; +import org.junit.Test; + +import java.io.IOException; +import java.net.URL; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class AccessorTest { + private Pattern accessorMethodPattern = Pattern.compile("([a-zA-Z]*)_([a-zA-Z]*)"); + + private static final Map operationTypes; + + static { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put("postinc", SyntheticAccessorResolver.POSTFIX_INCREMENT); + builder.put("preinc", SyntheticAccessorResolver.PREFIX_INCREMENT); + builder.put("postdec", SyntheticAccessorResolver.POSTFIX_DECREMENT); + builder.put("predec", SyntheticAccessorResolver.PREFIX_DECREMENT); + builder.put("add", SyntheticAccessorResolver.ADD_ASSIGNMENT); + builder.put("sub", SyntheticAccessorResolver.SUB_ASSIGNMENT); + builder.put("mul", SyntheticAccessorResolver.MUL_ASSIGNMENT); + builder.put("div", SyntheticAccessorResolver.DIV_ASSIGNMENT); + builder.put("rem", SyntheticAccessorResolver.REM_ASSIGNMENT); + builder.put("and", SyntheticAccessorResolver.AND_ASSIGNMENT); + builder.put("or", SyntheticAccessorResolver.OR_ASSIGNMENT); + builder.put("xor", SyntheticAccessorResolver.XOR_ASSIGNMENT); + builder.put("shl", SyntheticAccessorResolver.SHL_ASSIGNMENT); + builder.put("shr", SyntheticAccessorResolver.SHR_ASSIGNMENT); + builder.put("ushr", SyntheticAccessorResolver.USHR_ASSIGNMENT); + operationTypes = builder.build(); + } + + @Test + public void testAccessors() throws IOException { + URL url = AccessorTest.class.getClassLoader().getResource("accessorTest.dex"); + Assert.assertNotNull(url); + DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15); + + SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getClasses()); + + ClassDef accessorTypesClass = null; + ClassDef accessorsClass = null; + + for (ClassDef classDef: f.getClasses()) { + String className = classDef.getType(); + + if (className.equals("Lorg/jf/dexlib2/AccessorTypes;")) { + accessorTypesClass = classDef; + } else if (className.equals("Lorg/jf/dexlib2/AccessorTypes$Accessors;")) { + accessorsClass = classDef; + } + } + + Assert.assertNotNull(accessorTypesClass); + Assert.assertNotNull(accessorsClass); + + for (Method method: accessorsClass.getMethods()) { + Matcher m = accessorMethodPattern.matcher(method.getName()); + if (!m.matches()) { + continue; + } + String type = m.group(1); + String operation = m.group(2); + + MethodImplementation methodImpl = method.getImplementation(); + Assert.assertNotNull(methodImpl); + + for (Instruction instruction: methodImpl.getInstructions()) { + Opcode opcode = instruction.getOpcode(); + if (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) { + MethodReference accessorMethod = + (MethodReference)((ReferenceInstruction) instruction).getReference(); + + SyntheticAccessorResolver.AccessedMember accessedMember = sar.getAccessedMember(accessorMethod); + + Assert.assertNotNull(String.format("Could not resolve accessor for %s_%s", type, operation), + accessedMember); + + int operationType = operationTypes.get(operation); + Assert.assertEquals(operationType, accessedMember.accessedMemberType); + + Assert.assertEquals(String.format("%s_val", type), + ((FieldReference)accessedMember.accessedMember).getName()); + } + } + } + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AccessFlags.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/AccessFlags.java similarity index 80% rename from brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AccessFlags.java rename to brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/AccessFlags.java index b0e2f79e..d3cf74c8 100644 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Util/AccessFlags.java +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/AccessFlags.java @@ -1,32 +1,35 @@ /* - * [The "BSD licence"] - * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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: - * 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. + * modification, are permitted provided that the following conditions are + * met: * - * 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, + * * 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. + * (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.Util; +package org.jf.dexlib2; import java.util.HashMap; @@ -81,6 +84,10 @@ public enum AccessFlags this.validForField = validForField; } + public boolean isSet(int accessFlags) { + return (this.value & accessFlags) != 0; + } + public static AccessFlags[] getAccessFlagsForClass(int accessFlagValue) { int size = 0; for (AccessFlags accessFlag: allFlags) { diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/AnnotationVisibility.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/AnnotationVisibility.java new file mode 100644 index 00000000..06c4677e --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/AnnotationVisibility.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +import org.jf.util.ExceptionWithContext; + +public final class AnnotationVisibility { + public static final int BUILD = 0; + public static final int RUNTIME = 1; + public static final int SYSTEM = 2; + + private static String[] NAMES = new String[] {"build", "runtime", "system"}; + + public static String getVisibility(int visibility) { + if (visibility < 0 || visibility >= NAMES.length) { + throw new ExceptionWithContext("Invalid annotation visibility %d", visibility); + } + return NAMES[visibility]; + } + + public static int getVisibility(String visibility) { + visibility = visibility.toLowerCase(); + if (visibility.equals("build")) { + return BUILD; + } + if (visibility.equals("runtime")) { + return RUNTIME; + } + if (visibility.equals("system")) { + return SYSTEM; + } + throw new ExceptionWithContext("Invalid annotation visibility: %s", visibility); + } + + private AnnotationVisibility() {} +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/DebugItemType.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/DebugItemType.java new file mode 100644 index 00000000..7f4d8876 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/DebugItemType.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +public final class DebugItemType { + // The debug items that directly correspond with one of the dexlib2.iface.debug interfaces + public static final int START_LOCAL = 0x03; + public static final int END_LOCAL = 0x05; + public static final int RESTART_LOCAL = 0x06; + public static final int PROLOGUE_END = 0x07; + public static final int EPILOGUE_BEGIN = 0x08; + public static final int SET_SOURCE_FILE = 0x09; + public static final int LINE_NUMBER = 0x0a; + + // Other items, which are typically handled internally + public static final int END_SEQUENCE = 0x00; + public static final int ADVANCE_PC = 0x01; + public static final int ADVANCE_LINE = 0x02; + public static final int START_LOCAL_EXTENDED = 0x04; + + private DebugItemType() {} +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java new file mode 100644 index 00000000..5987d7ce --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java @@ -0,0 +1,133 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +import com.google.common.io.ByteStreams; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.dexbacked.DexBackedOdexFile; +import org.jf.dexlib2.iface.DexFile; +import org.jf.dexlib2.writer.pool.DexPool; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.io.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public final class DexFileFactory { + @Nonnull + public static DexBackedDexFile loadDexFile(String path, int api) throws IOException { + return loadDexFile(new File(path), new Opcodes(api)); + } + + @Nonnull + public static DexBackedDexFile loadDexFile(File dexFile, int api) throws IOException { + return loadDexFile(dexFile, new Opcodes(api)); + } + + @Nonnull + public static DexBackedDexFile loadDexFile(File dexFile, @Nonnull Opcodes opcodes) throws IOException { + ZipFile zipFile = null; + boolean isZipFile = false; + try { + zipFile = new ZipFile(dexFile); + // if we get here, it's safe to assume we have a zip file + isZipFile = true; + + ZipEntry zipEntry = zipFile.getEntry("classes.dex"); + if (zipEntry == null) { + throw new NoClassesDexException("zip file %s does not contain a classes.dex file", dexFile.getName()); + } + long fileLength = zipEntry.getSize(); + if (fileLength < 40) { + throw new ExceptionWithContext( + "The classes.dex file in %s is too small to be a valid dex file", dexFile.getName()); + } else if (fileLength > Integer.MAX_VALUE) { + throw new ExceptionWithContext("The classes.dex file in %s is too large to read in", dexFile.getName()); + } + byte[] dexBytes = new byte[(int)fileLength]; + ByteStreams.readFully(zipFile.getInputStream(zipEntry), dexBytes); + return new DexBackedDexFile(opcodes, dexBytes); + } catch (IOException ex) { + // don't continue on if we know it's a zip file + if (isZipFile) { + throw ex; + } + } finally { + if (zipFile != null) { + try { + zipFile.close(); + } catch (IOException ex) { + // just eat it + } + } + } + + InputStream inputStream = new BufferedInputStream(new FileInputStream(dexFile)); + + try { + return DexBackedDexFile.fromInputStream(opcodes, inputStream); + } catch (DexBackedDexFile.NotADexFile ex) { + // just eat it + } + + // Note: DexBackedDexFile.fromInputStream will reset inputStream back to the same position, if it fails + + try { + return DexBackedOdexFile.fromInputStream(opcodes, inputStream); + } catch (DexBackedOdexFile.NotAnOdexFile ex) { + // just eat it + } + + throw new ExceptionWithContext("%s is not an apk, dex file or odex file.", dexFile.getPath()); + } + + public static void writeDexFile(String path, DexFile dexFile) throws IOException { + DexPool.writeTo(path, dexFile); + } + + private DexFileFactory() {} + + public static class NoClassesDexException extends ExceptionWithContext { + public NoClassesDexException(Throwable cause) { + super(cause); + } + + public NoClassesDexException(Throwable cause, String message, Object... formatArgs) { + super(cause, message, formatArgs); + } + + public NoClassesDexException(String message, Object... formatArgs) { + super(message, formatArgs); + } + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Format.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Format.java new file mode 100644 index 00000000..d91b7432 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Format.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +public enum Format { + Format10t(2), + Format10x(2), + Format11n(2), + Format11x(2), + Format12x(2), + Format20bc(4), + Format20t(4), + Format21c(4), + Format21ih(4), + Format21lh(4), + Format21s(4), + Format21t(4), + Format22b(4), + Format22c(4), + Format22cs(4), + Format22s(4), + Format22t(4), + Format22x(4), + Format23x(4), + Format30t(6), + Format31c(6), + Format31i(6), + Format31t(6), + Format32x(6), + Format35c(6), + Format35mi(6), + Format35ms(6), + Format3rc(6), + Format3rmi(6), + Format3rms(6), + Format51l(10), + ArrayPayload(-1, true), + PackedSwitchPayload(-1, true), + SparseSwitchPayload(-1, true), + UnresolvedOdexInstruction(-1); + + public final int size; + public final boolean isPayloadFormat; + + private Format(int size) { + this(size, false); + } + + private Format(int size, boolean isPayloadFormat) { + this.size = size; + this.isPayloadFormat = isPayloadFormat; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java new file mode 100644 index 00000000..d0adee8c --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java @@ -0,0 +1,420 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +public enum Opcode +{ + NOP((short)0x00, "nop", ReferenceType.NONE, Format.Format10x, Opcode.CAN_CONTINUE), + MOVE((short)0x01, "move", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MOVE_FROM16((short)0x02, "move/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MOVE_16((short)0x03, "move/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MOVE_WIDE((short)0x04, "move-wide", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + MOVE_WIDE_FROM16((short)0x05, "move-wide/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + MOVE_WIDE_16((short)0x06, "move-wide/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + MOVE_OBJECT((short)0x07, "move-object", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MOVE_OBJECT_FROM16((short)0x08, "move-object/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MOVE_OBJECT_16((short)0x09, "move-object/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MOVE_RESULT((short)0x0a, "move-result", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MOVE_RESULT_WIDE((short)0x0b, "move-result-wide", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + MOVE_RESULT_OBJECT((short)0x0c, "move-result-object", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MOVE_EXCEPTION((short)0x0d, "move-exception", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + RETURN_VOID((short)0x0e, "return-void", ReferenceType.NONE, Format.Format10x), + RETURN((short)0x0f, "return", ReferenceType.NONE, Format.Format11x), + RETURN_WIDE((short)0x10, "return-wide", ReferenceType.NONE, Format.Format11x), + RETURN_OBJECT((short)0x11, "return-object", ReferenceType.NONE, Format.Format11x), + CONST_4((short)0x12, "const/4", ReferenceType.NONE, Format.Format11n, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CONST_16((short)0x13, "const/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CONST((short)0x14, "const", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CONST_HIGH16((short)0x15, "const/high16", ReferenceType.NONE, Format.Format21ih, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CONST_WIDE_16((short)0x16, "const-wide/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + CONST_WIDE_32((short)0x17, "const-wide/32", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + CONST_WIDE((short)0x18, "const-wide", ReferenceType.NONE, Format.Format51l, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + CONST_WIDE_HIGH16((short)0x19, "const-wide/high16", ReferenceType.NONE, Format.Format21lh, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + CONST_STRING((short)0x1a, "const-string", ReferenceType.STRING, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0x1b), + CONST_STRING_JUMBO((short)0x1b, "const-string/jumbo", ReferenceType.STRING, Format.Format31c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CONST_CLASS((short)0x1c, "const-class", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MONITOR_ENTER((short)0x1d, "monitor-enter", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + MONITOR_EXIT((short)0x1e, "monitor-exit", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + CHECK_CAST((short)0x1f, "check-cast", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + INSTANCE_OF((short)0x20, "instance-of", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + ARRAY_LENGTH((short)0x21, "array-length", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + NEW_INSTANCE((short)0x22, "new-instance", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + NEW_ARRAY((short)0x23, "new-array", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + FILLED_NEW_ARRAY((short)0x24, "filled-new-array", ReferenceType.TYPE, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + FILLED_NEW_ARRAY_RANGE((short)0x25, "filled-new-array/range", ReferenceType.TYPE, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + FILL_ARRAY_DATA((short)0x26, "fill-array-data", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE), + THROW((short)0x27, "throw", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW), + GOTO((short)0x28, "goto", ReferenceType.NONE, Format.Format10t), + GOTO_16((short)0x29, "goto/16", ReferenceType.NONE, Format.Format20t), + GOTO_32((short)0x2a, "goto/32", ReferenceType.NONE, Format.Format30t), + PACKED_SWITCH((short)0x2b, "packed-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE), + SPARSE_SWITCH((short)0x2c, "sparse-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE), + CMPL_FLOAT((short)0x2d, "cmpl-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CMPG_FLOAT((short)0x2e, "cmpg-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CMPL_DOUBLE((short)0x2f, "cmpl-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CMPG_DOUBLE((short)0x30, "cmpg-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + CMP_LONG((short)0x31, "cmp-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IF_EQ((short)0x32, "if-eq", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE), + IF_NE((short)0x33, "if-ne", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE), + IF_LT((short)0x34, "if-lt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE), + IF_GE((short)0x35, "if-ge", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE), + IF_GT((short)0x36, "if-gt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE), + IF_LE((short)0x37, "if-le", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE), + IF_EQZ((short)0x38, "if-eqz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE), + IF_NEZ((short)0x39, "if-nez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE), + IF_LTZ((short)0x3a, "if-ltz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE), + IF_GEZ((short)0x3b, "if-gez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE), + IF_GTZ((short)0x3c, "if-gtz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE), + IF_LEZ((short)0x3d, "if-lez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE), + AGET((short)0x44, "aget", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AGET_WIDE((short)0x45, "aget-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + AGET_OBJECT((short)0x46, "aget-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AGET_BOOLEAN((short)0x47, "aget-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AGET_BYTE((short)0x48, "aget-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AGET_CHAR((short)0x49, "aget-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AGET_SHORT((short)0x4a, "aget-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + APUT((short)0x4b, "aput", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + APUT_WIDE((short)0x4c, "aput-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + APUT_OBJECT((short)0x4d, "aput-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + APUT_BOOLEAN((short)0x4e, "aput-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + APUT_BYTE((short)0x4f, "aput-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + APUT_CHAR((short)0x50, "aput-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + APUT_SHORT((short)0x51, "aput-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IGET((short)0x52, "iget", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_WIDE((short)0x53, "iget-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + IGET_OBJECT((short)0x54, "iget-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_BOOLEAN((short)0x55, "iget-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_BYTE((short)0x56, "iget-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_CHAR((short)0x57, "iget-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_SHORT((short)0x58, "iget-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IPUT((short)0x59, "iput", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_WIDE((short)0x5a, "iput-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_OBJECT((short)0x5b, "iput-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_BOOLEAN((short)0x5c, "iput-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_BYTE((short)0x5d, "iput-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_CHAR((short)0x5e, "iput-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_SHORT((short)0x5f, "iput-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SGET((short)0x60, "sget", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SGET_WIDE((short)0x61, "sget-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SGET_OBJECT((short)0x62, "sget-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SGET_BOOLEAN((short)0x63, "sget-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SGET_BYTE((short)0x64, "sget-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SGET_CHAR((short)0x65, "sget-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SGET_SHORT((short)0x66, "sget-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SPUT((short)0x67, "sput", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SPUT_WIDE((short)0x68, "sput-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SPUT_OBJECT((short)0x69, "sput-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SPUT_BOOLEAN((short)0x6a, "sput-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SPUT_BYTE((short)0x6b, "sput-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SPUT_CHAR((short)0x6c, "sput-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SPUT_SHORT((short)0x6d, "sput-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + INVOKE_VIRTUAL((short)0x6e, "invoke-virtual", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_SUPER((short)0x6f, "invoke-super", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_DIRECT((short)0x70, "invoke-direct", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE), + INVOKE_STATIC((short)0x71, "invoke-static", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_INTERFACE((short)0x72, "invoke-interface", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_VIRTUAL_RANGE((short)0x74, "invoke-virtual/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_SUPER_RANGE((short)0x75, "invoke-super/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_DIRECT_RANGE((short)0x76, "invoke-direct/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE), + INVOKE_STATIC_RANGE((short)0x77, "invoke-static/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_INTERFACE_RANGE((short)0x78, "invoke-interface/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + NEG_INT((short)0x7b, "neg-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + NOT_INT((short)0x7c, "not-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + NEG_LONG((short)0x7d, "neg-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + NOT_LONG((short)0x7e, "not-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + NEG_FLOAT((short)0x7f, "neg-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + NEG_DOUBLE((short)0x80, "neg-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + INT_TO_LONG((short)0x81, "int-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + INT_TO_FLOAT((short)0x82, "int-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + INT_TO_DOUBLE((short)0x83, "int-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + LONG_TO_INT((short)0x84, "long-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + LONG_TO_FLOAT((short)0x85, "long-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + LONG_TO_DOUBLE((short)0x86, "long-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + FLOAT_TO_INT((short)0x87, "float-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + FLOAT_TO_LONG((short)0x88, "float-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + FLOAT_TO_DOUBLE((short)0x89, "float-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + DOUBLE_TO_INT((short)0x8a, "double-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + DOUBLE_TO_LONG((short)0x8b, "double-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + DOUBLE_TO_FLOAT((short)0x8c, "double-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + INT_TO_BYTE((short)0x8d, "int-to-byte", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + INT_TO_CHAR((short)0x8e, "int-to-char", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + INT_TO_SHORT((short)0x8f, "int-to-short", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + ADD_INT((short)0x90, "add-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SUB_INT((short)0x91, "sub-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MUL_INT((short)0x92, "mul-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + DIV_INT((short)0x93, "div-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + REM_INT((short)0x94, "rem-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AND_INT((short)0x95, "and-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + OR_INT((short)0x96, "or-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + XOR_INT((short)0x97, "xor-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SHL_INT((short)0x98, "shl-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SHR_INT((short)0x99, "shr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + USHR_INT((short)0x9a, "ushr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + ADD_LONG((short)0x9b, "add-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SUB_LONG((short)0x9c, "sub-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + MUL_LONG((short)0x9d, "mul-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + DIV_LONG((short)0x9e, "div-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + REM_LONG((short)0x9f, "rem-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + AND_LONG((short)0xa0, "and-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + OR_LONG((short)0xa1, "or-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + XOR_LONG((short)0xa2, "xor-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SHL_LONG((short)0xa3, "shl-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SHR_LONG((short)0xa4, "shr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + USHR_LONG((short)0xa5, "ushr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + ADD_FLOAT((short)0xa6, "add-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SUB_FLOAT((short)0xa7, "sub-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MUL_FLOAT((short)0xa8, "mul-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + DIV_FLOAT((short)0xa9, "div-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + REM_FLOAT((short)0xaa, "rem-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + ADD_DOUBLE((short)0xab, "add-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SUB_DOUBLE((short)0xac, "sub-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + MUL_DOUBLE((short)0xad, "mul-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + DIV_DOUBLE((short)0xae, "div-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + REM_DOUBLE((short)0xaf, "rem-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + ADD_INT_2ADDR((short)0xb0, "add-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SUB_INT_2ADDR((short)0xb1, "sub-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MUL_INT_2ADDR((short)0xb2, "mul-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + DIV_INT_2ADDR((short)0xb3, "div-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + REM_INT_2ADDR((short)0xb4, "rem-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AND_INT_2ADDR((short)0xb5, "and-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + OR_INT_2ADDR((short)0xb6, "or-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + XOR_INT_2ADDR((short)0xb7, "xor-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SHL_INT_2ADDR((short)0xb8, "shl-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SHR_INT_2ADDR((short)0xb9, "shr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + USHR_INT_2ADDR((short)0xba, "ushr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + ADD_LONG_2ADDR((short)0xbb, "add-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SUB_LONG_2ADDR((short)0xbc, "sub-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + MUL_LONG_2ADDR((short)0xbd, "mul-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + DIV_LONG_2ADDR((short)0xbe, "div-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + REM_LONG_2ADDR((short)0xbf, "rem-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + AND_LONG_2ADDR((short)0xc0, "and-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + OR_LONG_2ADDR((short)0xc1, "or-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + XOR_LONG_2ADDR((short)0xc2, "xor-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SHL_LONG_2ADDR((short)0xc3, "shl-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SHR_LONG_2ADDR((short)0xc4, "shr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + USHR_LONG_2ADDR((short)0xc5, "ushr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + ADD_FLOAT_2ADDR((short)0xc6, "add-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SUB_FLOAT_2ADDR((short)0xc7, "sub-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MUL_FLOAT_2ADDR((short)0xc8, "mul-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + DIV_FLOAT_2ADDR((short)0xc9, "div-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + REM_FLOAT_2ADDR((short)0xca, "rem-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + ADD_DOUBLE_2ADDR((short)0xcb, "add-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SUB_DOUBLE_2ADDR((short)0xcc, "sub-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + MUL_DOUBLE_2ADDR((short)0xcd, "mul-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + DIV_DOUBLE_2ADDR((short)0xce, "div-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + REM_DOUBLE_2ADDR((short)0xcf, "rem-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + ADD_INT_LIT16((short)0xd0, "add-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + RSUB_INT((short)0xd1, "rsub-int", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MUL_INT_LIT16((short)0xd2, "mul-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + DIV_INT_LIT16((short)0xd3, "div-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + REM_INT_LIT16((short)0xd4, "rem-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AND_INT_LIT16((short)0xd5, "and-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + OR_INT_LIT16((short)0xd6, "or-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + XOR_INT_LIT16((short)0xd7, "xor-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + ADD_INT_LIT8((short)0xd8, "add-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + RSUB_INT_LIT8((short)0xd9, "rsub-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + MUL_INT_LIT8((short)0xda, "mul-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + DIV_INT_LIT8((short)0xdb, "div-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + REM_INT_LIT8((short)0xdc, "rem-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + AND_INT_LIT8((short)0xdd, "and-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + OR_INT_LIT8((short)0xde, "or-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + XOR_INT_LIT8((short)0xdf, "xor-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SHL_INT_LIT8((short)0xe0, "shl-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SHR_INT_LIT8((short)0xe1, "shr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + USHR_INT_LIT8((short)0xe2, "ushr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + + IGET_VOLATILE((short)0xe3, "iget-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IPUT_VOLATILE((short)0xe4, "iput-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SGET_VOLATILE((short)0xe5, "sget-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SPUT_VOLATILE((short)0xe6, "sput-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IGET_OBJECT_VOLATILE((short)0xe7, "iget-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_WIDE_VOLATILE((short)0xe8, "iget-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + IPUT_WIDE_VOLATILE((short)0xe9, "iput-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SGET_WIDE_VOLATILE((short)0xea, "sget-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + SPUT_WIDE_VOLATILE((short)0xeb, "sput-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + + THROW_VERIFICATION_ERROR((short)0xed, "throw-verification-error", minApi(5), ReferenceType.NONE, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW), + EXECUTE_INLINE((short)0xee, "execute-inline", ReferenceType.NONE, Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + EXECUTE_INLINE_RANGE((short)0xef, "execute-inline/range", minApi(8), ReferenceType.NONE, Format.Format3rmi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_DIRECT_EMPTY((short)0xf0, "invoke-direct-empty", maxApi(13), ReferenceType.METHOD, Format.Format35c, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE), + INVOKE_OBJECT_INIT_RANGE((short)0xf0, "invoke-object-init/range", minApi(14), ReferenceType.METHOD, Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE), + RETURN_VOID_BARRIER((short)0xf1, "return-void-barrier", minApi(11), ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY), + IGET_QUICK((short)0xf2, "iget-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), + IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + IPUT_QUICK((short)0xf5, "iput-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_WIDE_QUICK((short)0xf6, "iput-wide-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + IPUT_OBJECT_QUICK((short)0xf7, "iput-object-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + INVOKE_VIRTUAL_QUICK((short)0xf8, "invoke-virtual-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_VIRTUAL_QUICK_RANGE((short)0xf9, "invoke-virtual-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_SUPER_QUICK((short)0xfa, "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_SUPER_QUICK_RANGE((short)0xfb, "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + + IPUT_OBJECT_VOLATILE((short)0xfc, "iput-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + SGET_OBJECT_VOLATILE((short)0xfd, "sget-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), + SPUT_OBJECT_VOLATILE((short)0xfe, "sput-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE), + + PACKED_SWITCH_PAYLOAD((short)0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0), + SPARSE_SWITCH_PAYLOAD((short)0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0), + ARRAY_PAYLOAD((short)0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0); + + //if the instruction can throw an exception + public static final int CAN_THROW = 0x1; + //if the instruction is an odex only instruction + public static final int ODEX_ONLY = 0x2; + //if execution can continue to the next instruction + public static final int CAN_CONTINUE = 0x4; + //if the instruction sets the "hidden" result register + public static final int SETS_RESULT = 0x8; + //if the instruction sets the value of it's first register + public static final int SETS_REGISTER = 0x10; + //if the instruction sets the value of it's first register to a wide type + public static final int SETS_WIDE_REGISTER = 0x20; + //if the instruction is an odexed iget-quick/iput-quick instruction + public static final int ODEXED_INSTANCE_QUICK = 0x40; + //if the instruction is an odexed iget-volatile/iput-volatile instruction + public static final int ODEXED_INSTANCE_VOLATILE = 0x80; + //if the instruction is an odexed sget-volatile/sput-volatile instruction + public static final int ODEXED_STATIC_VOLATILE = 0x100; + //if the instruction is a jumbo instruction + public static final int JUMBO_OPCODE = 0x200; + //if the instruction can initialize an uninitialized object reference + public static final int CAN_INITIALIZE_REFERENCE = 0x400; + + private static final int ALL_APIS = 0xFFFF0000; + + private static int minApi(int api) { + return 0xFFFF0000 | (api & 0xFFFF); + } + + private static int maxApi(int api) { + return api << 16; + } + + public final short value; + public final String name; + // high 16-bits is the max api, low 16-bits is the min api + public final int apiConstraints; + public final int referenceType; + public final Format format; + public final int flags; + + Opcode(short opcodeValue, String opcodeName, int referenceType, Format format) { + this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, 0, (short)-1); + } + + Opcode(short opcodeValue, String opcodeName, int referenceType, Format format, int flags) { + this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, flags, (short)-1); + } + + Opcode(short opcodeValue, String opcodeName, int referenceType, Format format, int flags, short jumboOpcodeValue) { + this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, flags, jumboOpcodeValue); + } + + Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format) { + this(opcodeValue, opcodeName, apiConstraints, referenceType, format, 0, (short)-1); + } + + Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format, int flags) { + this(opcodeValue, opcodeName, apiConstraints, referenceType, format, flags, (short)-1); + } + + Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format, int flags, + short jumboOpcodeValue) { + this.value = opcodeValue; + this.name = opcodeName; + this.apiConstraints = apiConstraints; + this.referenceType = referenceType; + this.format = format; + this.flags = flags; + // TODO: implement jumbo opcodes for dexlib2 and uncomment + // this.jumboOpcode = jumboOpcodeValue; + } + + /** + * @return the minimum api level that can use this opcode (inclusive) + */ + public int getMinApi() { + return apiConstraints & 0xFFFF; + } + + /** + * @return the maximum api level that can to use this opcode (inclusive) + */ + public int getMaxApi() { + return apiConstraints >>> 16; + } + + public final boolean canThrow() { + return (flags & CAN_THROW) != 0; + } + + public final boolean odexOnly() { + return (flags & ODEX_ONLY) != 0; + } + + public final boolean canContinue() { + return (flags & CAN_CONTINUE) != 0; + } + + public final boolean setsResult() { + return (flags & SETS_RESULT) != 0; + } + + public final boolean setsRegister() { + return (flags & SETS_REGISTER) != 0; + } + + public final boolean setsWideRegister() { + return (flags & SETS_WIDE_REGISTER) != 0; + } + + public final boolean isOdexedInstanceQuick() { + return (flags & ODEXED_INSTANCE_QUICK) != 0; + } + + public final boolean isOdexedInstanceVolatile() { + return (flags & ODEXED_INSTANCE_VOLATILE) != 0; + } + + public final boolean isOdexedStaticVolatile() { + return (flags & ODEXED_STATIC_VOLATILE) != 0; + } + + public final boolean isJumboOpcode() { + return (flags & JUMBO_OPCODE) != 0; + } + + public final boolean canInitializeReference() { + return (flags & CAN_INITIALIZE_REFERENCE) != 0; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java new file mode 100644 index 00000000..d6e5532e --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java @@ -0,0 +1,78 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +import com.google.common.collect.Maps; + +import javax.annotation.Nullable; +import java.util.HashMap; + +public class Opcodes { + private final Opcode[] opcodesByValue; + private final HashMap opcodesByName; + + public Opcodes(int api) { + opcodesByValue = new Opcode[256]; + opcodesByName = Maps.newHashMap(); + + for (Opcode opcode: Opcode.values()) { + if (!opcode.format.isPayloadFormat) { + if (api <= opcode.getMaxApi() && api >= opcode.getMinApi()) { + opcodesByValue[opcode.value] = opcode; + opcodesByName.put(opcode.name.toLowerCase(), opcode); + } + } + } + } + + @Nullable + public Opcode getOpcodeByName(String opcodeName) { + return opcodesByName.get(opcodeName.toLowerCase()); + } + + @Nullable + public Opcode getOpcodeByValue(int opcodeValue) { + switch (opcodeValue) { + case 0x100: + return Opcode.PACKED_SWITCH_PAYLOAD; + case 0x200: + return Opcode.SPARSE_SWITCH_PAYLOAD; + case 0x300: + return Opcode.ARRAY_PAYLOAD; + default: + if (opcodeValue >= 0 && opcodeValue < opcodesByValue.length) { + return opcodesByValue[opcodeValue]; + } + return null; + } + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java new file mode 100644 index 00000000..3371f818 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java @@ -0,0 +1,103 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +import org.jf.dexlib2.iface.reference.*; +import org.jf.util.ExceptionWithContext; + +public final class ReferenceType { + public static final int STRING = 0; + public static final int TYPE = 1; + public static final int FIELD = 2; + public static final int METHOD = 3; + public static final int NONE = 4; + + public static String toString(int referenceType) { + switch (referenceType) { + case STRING: + return "string"; + case TYPE: + return "type"; + case FIELD: + return "field"; + case METHOD: + return "method"; + default: + throw new InvalidReferenceTypeException(referenceType); + } + } + + public static int getReferenceType(Reference reference) { + if (reference instanceof StringReference) { + return STRING; + } else if (reference instanceof TypeReference) { + return TYPE; + } else if (reference instanceof FieldReference) { + return FIELD; + } else if (reference instanceof MethodReference) { + return METHOD; + } else { + throw new IllegalStateException("Invalid reference"); + } + } + + /** + * Validate a specific reference type. Note that the NONE placeholder is specifically not considered valid here. + * + * @throws InvalidReferenceTypeException + */ + public static void validateReferenceType(int referenceType) { + if (referenceType < 0 || referenceType > 3) { + throw new InvalidReferenceTypeException(referenceType); + } + } + + public static class InvalidReferenceTypeException extends ExceptionWithContext { + private final int referenceType; + + public InvalidReferenceTypeException(int referenceType) { + super("Invalid reference type: %d", referenceType); + this.referenceType = referenceType; + } + + public InvalidReferenceTypeException(int referenceType, String message, Object... formatArgs) { + super(message, formatArgs); + this.referenceType = referenceType; + } + + public int getReferenceType() { + return referenceType; + } + } + + private ReferenceType() {} +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ValueType.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ValueType.java new file mode 100644 index 00000000..08710a6b --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/ValueType.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +public final class ValueType { + public static final int BYTE = 0x00; + public static final int SHORT = 0x02; + public static final int CHAR = 0x03; + public static final int INT = 0x04; + public static final int LONG = 0x06; + public static final int FLOAT = 0x10; + public static final int DOUBLE = 0x11; + public static final int STRING = 0x17; + public static final int TYPE = 0x18; + public static final int FIELD = 0x19; + public static final int METHOD = 0x1a; + public static final int ENUM = 0x1b; + public static final int ARRAY = 0x1c; + public static final int ANNOTATION = 0x1d; + public static final int NULL = 0x1e; + public static final int BOOLEAN = 0x1f; + + private ValueType() {} +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/VerificationError.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/VerificationError.java new file mode 100644 index 00000000..d0aa0297 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/VerificationError.java @@ -0,0 +1,102 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2; + +import com.google.common.collect.Maps; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nullable; +import java.util.HashMap; + +public class VerificationError { + public static final int GENERIC = 1; + public static final int NO_SUCH_CLASS = 2; + public static final int NO_SUCH_FIELD = 3; + public static final int NO_SUCH_METHOD = 4; + public static final int ILLEGAL_CLASS_ACCESS = 5; + public static final int ILLEGAL_FIELD_ACCESS = 6; + public static final int ILLEGAL_METHOD_ACCESS = 7; + public static final int CLASS_CHANGE_ERROR = 8; + public static final int INSTANTIATION_ERROR = 9; + + private static final HashMap verificationErrorNames = Maps.newHashMap(); + + static { + verificationErrorNames.put("generic-error", GENERIC); + verificationErrorNames.put("no-such-class", NO_SUCH_CLASS); + verificationErrorNames.put("no-such-field", NO_SUCH_FIELD); + verificationErrorNames.put("no-such-method", NO_SUCH_METHOD); + verificationErrorNames.put("illegal-class-access", ILLEGAL_CLASS_ACCESS); + verificationErrorNames.put("illegal-field-access", ILLEGAL_FIELD_ACCESS); + verificationErrorNames.put("illegal-method-access", ILLEGAL_METHOD_ACCESS); + verificationErrorNames.put("class-change-error", CLASS_CHANGE_ERROR); + verificationErrorNames.put("instantiation-error", INSTANTIATION_ERROR); + } + + @Nullable + public static String getVerificationErrorName(int verificationError) { + switch (verificationError) { + case GENERIC: + return "generic-error"; + case NO_SUCH_CLASS: + return "no-such-class"; + case NO_SUCH_FIELD: + return "no-such-field"; + case NO_SUCH_METHOD: + return "no-such-method"; + case ILLEGAL_CLASS_ACCESS: + return "illegal-class-access"; + case ILLEGAL_FIELD_ACCESS: + return "illegal-field-access"; + case ILLEGAL_METHOD_ACCESS: + return "illegal-method-access"; + case CLASS_CHANGE_ERROR: + return "class-change-error"; + case INSTANTIATION_ERROR: + return "instantiation-error"; + default: + return null; + } + } + + public static int getVerificationError(String verificationError) { + Integer ret = verificationErrorNames.get(verificationError); + if (ret == null) { + throw new ExceptionWithContext("Invalid verification error: %s", verificationError); + } + return ret; + } + + public static boolean isValidVerificationError(int verificationError) { + return verificationError > 0 && verificationError < 10; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalysisException.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalysisException.java new file mode 100644 index 00000000..4ca7b138 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalysisException.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import org.jf.util.ExceptionWithContext; + +public class AnalysisException extends ExceptionWithContext { + public int codeAddress; + + public AnalysisException(Throwable cause) { + super(cause); + } + + public AnalysisException(Throwable cause, String message, Object... formatArgs) { + super(cause, message, formatArgs); + } + + public AnalysisException(String message, Object... formatArgs) { + super(message, formatArgs); + } +} diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java similarity index 76% rename from brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java rename to brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java index d73abd5a..30cc9067 100644 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/AnalyzedInstruction.java +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java @@ -1,39 +1,42 @@ /* - * [The "BSD licence"] - * Copyright (c) 2010 Ben Gruver (JesusFreke) + * 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: - * 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. + * modification, are permitted provided that the following conditions are + * met: * - * 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, + * * 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. + * (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; +package org.jf.dexlib2.analysis; -import org.jf.dexlib.Code.*; -import org.jf.dexlib.Item; -import org.jf.dexlib.ItemType; -import org.jf.dexlib.MethodIdItem; -import org.jf.dexlib.Util.ExceptionWithContext; +import org.jf.dexlib2.iface.instruction.*; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.Reference; +import org.jf.util.ExceptionWithContext; +import javax.annotation.Nonnull; import java.util.*; public class AnalyzedInstruction implements Comparable { @@ -73,20 +76,13 @@ public class AnalyzedInstruction implements Comparable { */ protected final Instruction originalInstruction; - /** - * An analyzed instruction's "deadness" is set during analysis (i.e. MethodAnalyzer.analyzer()). A dead instruction - * is one that the analyzer never reaches. This occurs either with natural "dead code" - code that simply has no - * code path that can ever reach it, or code that follows an odexed instruction that can't be deodexed. - */ - protected boolean dead = false; - public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) { this.instruction = instruction; this.originalInstruction = instruction; this.instructionIndex = instructionIndex; this.postRegisterMap = new RegisterType[registerCount]; this.preRegisterMap = new RegisterType[registerCount]; - RegisterType unknown = RegisterType.getRegisterType(RegisterType.Category.Unknown, null); + RegisterType unknown = RegisterType.getRegisterType(RegisterType.UNKNOWN, null); for (int i=0; i { } protected void setDeodexedInstruction(Instruction instruction) { - assert originalInstruction.opcode.odexOnly(); + assert originalInstruction.getOpcode().odexOnly(); this.instruction = instruction; } protected void restoreOdexedInstruction() { - assert originalInstruction.opcode.odexOnly(); + assert originalInstruction.getOpcode().odexOnly(); instruction = originalInstruction; } @@ -139,10 +135,6 @@ public class AnalyzedInstruction implements Comparable { return originalInstruction; } - public boolean isDead() { - return dead; - } - /** * Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction * that can be the first successfully executed instruction in the method. The first instruction is always a @@ -184,7 +176,7 @@ public class AnalyzedInstruction implements Comparable { RegisterType oldRegisterType = preRegisterMap[registerNumber]; RegisterType mergedRegisterType = oldRegisterType.merge(registerType); - if (mergedRegisterType == oldRegisterType) { + if (mergedRegisterType.equals(oldRegisterType)) { return false; } @@ -226,7 +218,7 @@ public class AnalyzedInstruction implements Comparable { assert registerType != null; RegisterType oldRegisterType = postRegisterMap[registerNumber]; - if (oldRegisterType == registerType) { + if (oldRegisterType.equals(registerType)) { return false; } @@ -236,30 +228,26 @@ public class AnalyzedInstruction implements Comparable { protected boolean isInvokeInit() { - if (instruction == null || !instruction.opcode.canInitializeReference()) { + if (instruction == null || !instruction.getOpcode().canInitializeReference()) { return false; } - //TODO: check access flags instead of name? + ReferenceInstruction instruction = (ReferenceInstruction)this.instruction; - InstructionWithReference instruction = (InstructionWithReference)this.instruction; - Item item = instruction.getReferencedItem(); - assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; - MethodIdItem method = (MethodIdItem)item; - - if (!method.getMethodName().getStringValue().equals("")) { - return false; + Reference reference = instruction.getReference(); + if (reference instanceof MethodReference) { + return ((MethodReference)reference).getName().equals(""); } - return true; + return false; } public boolean setsRegister() { - return instruction.opcode.setsRegister(); + return instruction.getOpcode().setsRegister(); } public boolean setsWideRegister() { - return instruction.opcode.setsWideRegister(); + return instruction.getOpcode().setsWideRegister(); } public boolean setsRegister(int registerNumber) { @@ -271,11 +259,11 @@ public class AnalyzedInstruction implements Comparable { if (isInvokeInit()) { int destinationRegister; if (instruction instanceof FiveRegisterInstruction) { - destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD(); + destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterC(); } else { assert instruction instanceof RegisterRangeInstruction; RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction; - assert rangeInstruction.getRegCount() > 0; + assert rangeInstruction.getRegisterCount() > 0; destinationRegister = rangeInstruction.getStartRegister(); } @@ -283,13 +271,13 @@ public class AnalyzedInstruction implements Comparable { return true; } RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(registerNumber); - if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef && - preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) { + if (preInstructionDestRegisterType.category != RegisterType.UNINIT_REF && + preInstructionDestRegisterType.category != RegisterType.UNINIT_THIS) { return false; } //check if the uninit ref has been copied to another register - if (getPreInstructionRegisterType(registerNumber) == preInstructionDestRegisterType) { + if (getPreInstructionRegisterType(registerNumber).equals(preInstructionDestRegisterType)) { return true; } return false; @@ -310,27 +298,28 @@ public class AnalyzedInstruction implements Comparable { } public int getDestinationRegister() { - if (!this.instruction.opcode.setsRegister()) { + if (!this.instruction.getOpcode().setsRegister()) { throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " + "store a value"); } - return ((SingleRegisterInstruction)instruction).getRegisterA(); + return ((OneRegisterInstruction)instruction).getRegisterA(); } public int getRegisterCount() { return postRegisterMap.length; } + @Nonnull public RegisterType getPostInstructionRegisterType(int registerNumber) { return postRegisterMap[registerNumber]; } + @Nonnull public RegisterType getPreInstructionRegisterType(int registerNumber) { return preRegisterMap[registerNumber]; } public int compareTo(AnalyzedInstruction analyzedInstruction) { - //TODO: out of curiosity, check the disassembly of this to see if it retrieves the value of analyzedInstruction.instructionIndex for every access. It should, because the field is final. What about if we set the field to non-final? if (instructionIndex < analyzedInstruction.instructionIndex) { return -1; } else if (instructionIndex == analyzedInstruction.instructionIndex) { diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java new file mode 100644 index 00000000..8fcfc8c5 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java @@ -0,0 +1,166 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import com.google.common.base.Strings; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.immutable.reference.ImmutableFieldReference; +import org.jf.dexlib2.util.TypeUtils; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ArrayProto implements TypeProto { + protected final ClassPath classPath; + protected final int dimensions; + protected final String elementType; + + public ArrayProto(@Nonnull ClassPath classPath, @Nonnull String type) { + this.classPath = classPath; + int i=0; + while (type.charAt(i) == '[') { + i++; + if (i == type.length()) { + throw new ExceptionWithContext("Invalid array type: %s", type); + } + } + + if (i == 0) { + throw new ExceptionWithContext("Invalid array type: %s", type); + } + + dimensions = i; + elementType = type.substring(i); + } + + @Override public String toString() { return getType(); } + @Nonnull @Override public ClassPath getClassPath() { return classPath; } + @Nonnull @Override public String getType() { return makeArrayType(elementType, dimensions); } + public int getDimensions() { return dimensions; } + @Override public boolean isInterface() { return false; } + + /** + * @return The base element type of this array. E.g. This would return Ljava/lang/String; for [[Ljava/lang/String; + */ + @Nonnull public String getElementType() { return elementType; } + + /** + * @return The immediate element type of this array. E.g. This would return [Ljava/lang/String; for + * [[Ljava/lang/String; + */ + @Nonnull public String getImmediateElementType() { + if (dimensions > 1) { + return makeArrayType(elementType, dimensions-1); + } + return elementType; + } + + @Override public boolean implementsInterface(@Nonnull String iface) { + return iface.equals("Ljava/lang/Cloneable;") || iface.equals("Ljava/io/Serializable;"); + } + + @Nullable @Override + public String getSuperclass() { + return "Ljava/lang/Object;"; + } + + @Nonnull @Override + public TypeProto getCommonSuperclass(@Nonnull TypeProto other) { + if (other instanceof ArrayProto) { + if (TypeUtils.isPrimitiveType(getElementType()) || + TypeUtils.isPrimitiveType(((ArrayProto)other).getElementType())) { + if (dimensions == ((ArrayProto)other).dimensions && + getElementType().equals(((ArrayProto)other).getElementType())) { + return this; + } + return classPath.getClass("Ljava/lang/Object;"); + } + + if (dimensions == ((ArrayProto)other).dimensions) { + TypeProto thisClass = classPath.getClass(elementType); + TypeProto otherClass = classPath.getClass(((ArrayProto)other).elementType); + TypeProto mergedClass = thisClass.getCommonSuperclass(otherClass); + if (thisClass == mergedClass) { + return this; + } + if (otherClass == mergedClass) { + return other; + } + return classPath.getClass(makeArrayType(mergedClass.getType(), dimensions)); + } + + int dimensions = Math.min(this.dimensions, ((ArrayProto)other).dimensions); + return classPath.getClass(makeArrayType("Ljava/lang/Object;", dimensions)); + } + + if (other instanceof ClassProto) { + try { + if (other.isInterface()) { + if (implementsInterface(other.getType())) { + return other; + } + } + } catch (UnresolvedClassException ex) { + // ignore + } + return classPath.getClass("Ljava/lang/Object;"); + } + + // otherwise, defer to the other class' getCommonSuperclass + return other.getCommonSuperclass(this); + } + + private static final String BRACKETS = Strings.repeat("[", 256); + + @Nonnull + private static String makeArrayType(@Nonnull String elementType, int dimensions) { + return BRACKETS.substring(0, dimensions) + elementType; + } + + + @Override + @Nullable + public FieldReference getFieldByOffset(int fieldOffset) { + if (fieldOffset==8) { + return new ImmutableFieldReference(getType(), "length", "int"); + } + return null; + } + + @Override + @Nullable + public MethodReference getMethodByVtableIndex(int vtableIndex) { + return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java new file mode 100644 index 00000000..aaf6b4de --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java @@ -0,0 +1,225 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.jf.dexlib2.DexFileFactory; +import org.jf.dexlib2.analysis.reflection.ReflectionClassDef; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.DexFile; +import org.jf.dexlib2.immutable.ImmutableDexFile; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ClassPath { + public static boolean dontLoadClassPath = false; + + @Nonnull private final TypeProto unknownClass; + @Nonnull private HashMap availableClasses = Maps.newHashMap(); + private int api; + + /** + * Creates a new ClassPath instance that can load classes from the given dex files + * + * @param classPath An array of DexFile objects. When loading a class, these dex files will be searched in order + */ + public ClassPath(DexFile... classPath) throws IOException { + this(Lists.newArrayList(classPath), 15); + } + + /** + * Creates a new ClassPath instance that can load classes from the given dex files + * + * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order + * @param api API level + */ + public ClassPath(@Nonnull Iterable classPath, int api) { + // add fallbacks for certain special classes that must be present + Iterable dexFiles = Iterables.concat(classPath, Lists.newArrayList(getBasicClasses())); + + unknownClass = new UnknownClassProto(this); + loadedClasses.put(unknownClass.getType(), unknownClass); + this.api = api; + + loadPrimitiveType("Z"); + loadPrimitiveType("B"); + loadPrimitiveType("S"); + loadPrimitiveType("C"); + loadPrimitiveType("I"); + loadPrimitiveType("J"); + loadPrimitiveType("F"); + loadPrimitiveType("D"); + loadPrimitiveType("L"); + + for (DexFile dexFile: dexFiles) { + for (ClassDef classDef: dexFile.getClasses()) { + ClassDef prev = availableClasses.get(classDef.getType()); + if (prev == null) { + availableClasses.put(classDef.getType(), classDef); + } + } + } + } + + private void loadPrimitiveType(String type) { + loadedClasses.put(type, new PrimitiveProto(this, type)); + } + + private static DexFile getBasicClasses() { + // fallbacks for some special classes that we assume are present + return new ImmutableDexFile(ImmutableSet.of( + new ReflectionClassDef(Class.class), + new ReflectionClassDef(Cloneable.class), + new ReflectionClassDef(Object.class), + new ReflectionClassDef(Serializable.class), + new ReflectionClassDef(String.class), + new ReflectionClassDef(Throwable.class))); + } + + @Nonnull + public TypeProto getClass(CharSequence type) { + return loadedClasses.getUnchecked(type.toString()); + } + + private final CacheLoader classLoader = new CacheLoader() { + @Override public TypeProto load(String type) throws Exception { + if (type.charAt(0) == '[') { + return new ArrayProto(ClassPath.this, type); + } else { + return new ClassProto(ClassPath.this, type); + } + } + }; + + @Nonnull private LoadingCache loadedClasses = CacheBuilder.newBuilder().build(classLoader); + + @Nonnull + public ClassDef getClassDef(String type) { + if (dontLoadClassPath) { + throw new UnresolvedClassException("Could not resolve class %s", type); + } + + ClassDef ret = availableClasses.get(type); + if (ret == null) { + throw new UnresolvedClassException("Could not resolve class %s", type); + } + return ret; + } + + @Nonnull + public TypeProto getUnknownClass() { + return unknownClass; + } + + public int getApi() { + return api; + } + + @Nonnull + public static ClassPath fromClassPath(Iterable classPathDirs, Iterable classPath, DexFile dexFile, + int api) { + ArrayList dexFiles = Lists.newArrayList(); + + for (String classPathEntry: classPath) { + dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api)); + } + dexFiles.add(dexFile); + return new ClassPath(dexFiles, api); + } + + private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$"); + + @Nonnull + private static DexFile loadClassPathEntry(@Nonnull Iterable classPathDirs, + @Nonnull String bootClassPathEntry, int api) { + File rawEntry = new File(bootClassPathEntry); + // strip off the path - we only care about the filename + String entryName = rawEntry.getName(); + + // if it's a dalvik-cache entry, grab the name of the jar/apk + if (entryName.endsWith("@classes.dex")) { + Matcher m = dalvikCacheOdexPattern.matcher(entryName); + + if (!m.find()) { + throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", bootClassPathEntry)); + } + + entryName = m.group(1); + } + + int extIndex = entryName.lastIndexOf("."); + + String baseEntryName; + if (extIndex == -1) { + baseEntryName = entryName; + } else { + baseEntryName = entryName.substring(0, extIndex); + } + + for (String classPathDir: classPathDirs) { + for (String ext: new String[]{"", ".odex", ".jar", ".apk", ".zip"}) { + File file = new File(classPathDir, baseEntryName + ext); + + if (file.exists() && file.isFile()) { + if (!file.canRead()) { + System.err.println(String.format( + "warning: cannot open %s for reading. Will continue looking.", file.getPath())); + } else { + try { + return DexFileFactory.loadDexFile(file, api); + } catch (DexFileFactory.NoClassesDexException ex) { + // ignore and continue + } catch (Exception ex) { + throw ExceptionWithContext.withContext(ex, + "Error while reading boot class path entry \"%s\"", bootClassPathEntry); + } + } + } + } + } + throw new ExceptionWithContext("Cannot locate boot class path file %s", bootClassPathEntry); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java new file mode 100644 index 00000000..0665f0c1 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java @@ -0,0 +1,675 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.jf.dexlib2.AccessFlags; +import org.jf.dexlib2.analysis.util.TypeProtoUtils; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.Field; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.immutable.ImmutableMethod; +import org.jf.util.ExceptionWithContext; +import org.jf.util.SparseArray; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields + * and their offsets. + */ +public class ClassProto implements TypeProto { + @Nonnull protected final ClassPath classPath; + @Nonnull protected final String type; + + protected boolean vtableFullyResolved = true; + protected boolean interfacesFullyResolved = true; + + public ClassProto(@Nonnull ClassPath classPath, @Nonnull String type) { + if (type.charAt(0) != 'L') { + throw new ExceptionWithContext("Cannot construct ClassProto for non reference type: %s", type); + } + this.classPath = classPath; + this.type = type; + } + + @Override public String toString() { return type; } + @Nonnull @Override public ClassPath getClassPath() { return classPath; } + @Nonnull @Override public String getType() { return type; } + + @Nonnull + public ClassDef getClassDef() { + return classDefSupplier.get(); + } + + + @Nonnull private final Supplier classDefSupplier = Suppliers.memoize(new Supplier() { + @Override public ClassDef get() { + return classPath.getClassDef(type); + } + }); + + /** + * Returns true if this class is an interface. + * + * If this class is not defined, then this will throw an UnresolvedClassException + * + * @return True if this class is an interface + */ + public boolean isInterface() { + ClassDef classDef = getClassDef(); + return (classDef.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0; + } + + /** + * Returns the set of interfaces that this class implements as a Map. + * + * The ClassDef value will be present only for the interfaces that this class directly implements (including any + * interfaces transitively implemented), but not for any interfaces that are only implemented by a superclass of + * this class + * + * For any interfaces that are only implemented by a superclass (or the class itself, if the class is an interface), + * the value will be null. + * + * If any interface couldn't be resolved, then the interfacesFullyResolved field will be set to false upon return. + * + * @return the set of interfaces that this class implements as a Map. + */ + @Nonnull + protected LinkedHashMap getInterfaces() { + return interfacesSupplier.get(); + } + + @Nonnull + private final Supplier> interfacesSupplier = + Suppliers.memoize(new Supplier>() { + @Override public LinkedHashMap get() { + LinkedHashMap interfaces = Maps.newLinkedHashMap(); + + try { + for (String interfaceType: getClassDef().getInterfaces()) { + if (!interfaces.containsKey(interfaceType)) { + ClassDef interfaceDef; + try { + interfaceDef = classPath.getClassDef(interfaceType); + interfaces.put(interfaceType, interfaceDef); + } catch (UnresolvedClassException ex) { + interfaces.put(interfaceType, null); + interfacesFullyResolved = false; + } + + ClassProto interfaceProto = (ClassProto) classPath.getClass(interfaceType); + for (String superInterface: interfaceProto.getInterfaces().keySet()) { + if (!interfaces.containsKey(superInterface)) { + interfaces.put(superInterface, interfaceProto.getInterfaces().get(superInterface)); + } + } + if (!interfaceProto.interfacesFullyResolved) { + interfacesFullyResolved = false; + } + } + } + } catch (UnresolvedClassException ex) { + interfacesFullyResolved = false; + } + + // now add self and super class interfaces, required for common super class lookup + // we don't really need ClassDef's for that, so let's just use null + + if (isInterface() && !interfaces.containsKey(getType())) { + interfaces.put(getType(), null); + } + + try { + String superclass = getSuperclass(); + if (superclass != null) { + ClassProto superclassProto = (ClassProto) classPath.getClass(superclass); + for (String superclassInterface: superclassProto.getInterfaces().keySet()) { + if (!interfaces.containsKey(superclassInterface)) { + interfaces.put(superclassInterface, null); + } + } + if (!superclassProto.interfacesFullyResolved) { + interfacesFullyResolved = false; + } + } + } catch (UnresolvedClassException ex) { + interfacesFullyResolved = false; + } + + return interfaces; + } + }); + + /** + * Gets the interfaces directly implemented by this class, or the interfaces they transitively implement. + * + * This does not include any interfaces that are only implemented by a superclass + * + * @return An iterables of ClassDefs representing the directly or transitively implemented interfaces + * @throws UnresolvedClassException if interfaces could not be fully resolved + */ + @Nonnull + protected Iterable getDirectInterfaces() { + Iterable directInterfaces = + FluentIterable.from(getInterfaces().values()).filter(Predicates.notNull()); + + if (!interfacesFullyResolved) { + throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); + } + + return directInterfaces; + } + + /** + * Checks if this class implements the given interface. + * + * If the interfaces of this class cannot be fully resolved then this + * method will either return true or throw an UnresolvedClassException + * + * @param iface The interface to check for + * @return true if this class implements the given interface, otherwise false + * @throws UnresolvedClassException if the interfaces for this class could not be fully resolved, and the interface + * is not one of the interfaces that were successfully resolved + */ + @Override + public boolean implementsInterface(@Nonnull String iface) { + if (getInterfaces().containsKey(iface)) { + return true; + } + if (!interfacesFullyResolved) { + throw new UnresolvedClassException("Interfaces for class %s not fully resolved", getType()); + } + return false; + } + + @Nullable @Override + public String getSuperclass() { + return getClassDef().getSuperclass(); + } + + /** + * This is a helper method for getCommonSuperclass + * + * It checks if this class is an interface, and if so, if other implements it. + * + * If this class is undefined, we go ahead and check if it is listed in other's interfaces. If not, we throw an + * UndefinedClassException + * + * If the interfaces of other cannot be fully resolved, we check the interfaces that can be resolved. If not found, + * we throw an UndefinedClassException + * + * @param other The class to check the interfaces of + * @return true if this class is an interface (or is undefined) other implements this class + * + */ + private boolean checkInterface(@Nonnull ClassProto other) { + boolean isResolved = true; + boolean isInterface = true; + try { + isInterface = isInterface(); + } catch (UnresolvedClassException ex) { + isResolved = false; + // if we don't know if this class is an interface or not, + // we can still try to call other.implementsInterface(this) + } + if (isInterface) { + try { + if (other.implementsInterface(getType())) { + return true; + } + } catch (UnresolvedClassException ex) { + // There are 2 possibilities here, depending on whether we were able to resolve this class. + // 1. If this class is resolved, then we know it is an interface class. The other class either + // isn't defined, or its interfaces couldn't be fully resolved. + // In this case, we throw an UnresolvedClassException + // 2. If this class is not resolved, we had tried to call implementsInterface anyway. We don't + // know for sure if this class is an interface or not. We return false, and let processing + // continue in getCommonSuperclass + if (isResolved) { + throw ex; + } + } + } + return false; + } + + @Override @Nonnull + public TypeProto getCommonSuperclass(@Nonnull TypeProto other) { + // use the other type's more specific implementation + if (!(other instanceof ClassProto)) { + return other.getCommonSuperclass(this); + } + + if (this == other || getType().equals(other.getType())) { + return this; + } + + if (this.getType().equals("Ljava/lang/Object;")) { + return this; + } + + if (other.getType().equals("Ljava/lang/Object;")) { + return other; + } + + boolean gotException = false; + try { + if (checkInterface((ClassProto)other)) { + return this; + } + } catch (UnresolvedClassException ex) { + gotException = true; + } + + try { + if (((ClassProto)other).checkInterface(this)) { + return other; + } + } catch (UnresolvedClassException ex) { + gotException = true; + } + if (gotException) { + return classPath.getUnknownClass(); + } + + List thisChain = Lists.newArrayList(this); + Iterables.addAll(thisChain, TypeProtoUtils.getSuperclassChain(this)); + + List otherChain = Lists.newArrayList(other); + Iterables.addAll(otherChain, TypeProtoUtils.getSuperclassChain(other)); + + // reverse them, so that the first entry is either Ljava/lang/Object; or Ujava/lang/Object; + thisChain = Lists.reverse(thisChain); + otherChain = Lists.reverse(otherChain); + + for (int i=Math.min(thisChain.size(), otherChain.size())-1; i>=0; i--) { + TypeProto typeProto = thisChain.get(i); + if (typeProto.getType().equals(otherChain.get(i).getType())) { + return typeProto; + } + } + + return classPath.getUnknownClass(); + } + + @Override + @Nullable + public FieldReference getFieldByOffset(int fieldOffset) { + if (getInstanceFields().size() == 0) { + return null; + } + return getInstanceFields().get(fieldOffset); + } + + @Override + @Nullable + public MethodReference getMethodByVtableIndex(int vtableIndex) { + List vtable = getVtable(); + if (vtableIndex < 0 || vtableIndex >= vtable.size()) { + return null; + } + + return vtable.get(vtableIndex); + } + + @Nonnull SparseArray getInstanceFields() { + return instanceFieldsSupplier.get(); + } + + @Nonnull private final Supplier> instanceFieldsSupplier = + Suppliers.memoize(new Supplier>() { + @Override public SparseArray get() { + //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to + //arrange fields, so that we end up with the same field offsets (which is needed for deodexing). + //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets() + + final byte REFERENCE = 0; + final byte WIDE = 1; + final byte OTHER = 2; + + ArrayList fields = getSortedInstanceFields(getClassDef()); + final int fieldCount = fields.size(); + //the "type" for each field in fields. 0=reference,1=wide,2=other + byte[] fieldTypes = new byte[fields.size()]; + for (int i=0; i front) { + if (fieldTypes[back] == REFERENCE) { + swap(fieldTypes, fields, front, back--); + break; + } + back--; + } + } + + if (fieldTypes[front] != REFERENCE) { + break; + } + } + + int startFieldOffset = 8; + String superclassType = getSuperclass(); + ClassProto superclass = null; + if (superclassType != null) { + superclass = (ClassProto) classPath.getClass(superclassType); + if (superclass != null) { + startFieldOffset = superclass.getNextFieldOffset(); + } + } + + int fieldIndexMod; + if ((startFieldOffset % 8) == 0) { + fieldIndexMod = 0; + } else { + fieldIndexMod = 1; + } + + //next, we need to group all the wide fields after the reference fields. But the wide fields have to be + //8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field + //is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in. + //If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets + if (front < fieldCount && (front % 2) != fieldIndexMod) { + if (fieldTypes[front] == WIDE) { + //we need to swap in a 32-bit field, so the wide fields will be correctly aligned + back = fieldCount - 1; + while (back > front) { + if (fieldTypes[back] == OTHER) { + swap(fieldTypes, fields, front++, back); + break; + } + back--; + } + } else { + //there's already a 32-bit field here that we can use + front++; + } + } + + //do the swap thing for wide fields + back = fieldCount - 1; + for (; front front) { + if (fieldTypes[back] == WIDE) { + swap(fieldTypes, fields, front, back--); + break; + } + back--; + } + } + + if (fieldTypes[front] != WIDE) { + break; + } + } + + SparseArray superFields; + if (superclass != null) { + superFields = superclass.getInstanceFields(); + } else { + superFields = new SparseArray(); + } + int superFieldCount = superFields.size(); + + //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets + int totalFieldCount = superFieldCount + fieldCount; + SparseArray instanceFields = new SparseArray(totalFieldCount); + + int fieldOffset; + + if (superclass != null && superFieldCount > 0) { + for (int i=0; i getSortedInstanceFields(@Nonnull ClassDef classDef) { + ArrayList fields = Lists.newArrayList(classDef.getInstanceFields()); + Collections.sort(fields); + return fields; + } + + private byte getFieldType(@Nonnull FieldReference field) { + switch (field.getType().charAt(0)) { + case '[': + case 'L': + return 0; //REFERENCE + case 'J': + case 'D': + return 1; //WIDE + default: + return 2; //OTHER + } + } + + private void swap(byte[] fieldTypes, List fields, int position1, int position2) { + byte tempType = fieldTypes[position1]; + fieldTypes[position1] = fieldTypes[position2]; + fieldTypes[position2] = tempType; + + Field tempField = fields.set(position1, fields.get(position2)); + fields.set(position2, tempField); + } + }); + + private int getNextFieldOffset() { + SparseArray instanceFields = getInstanceFields(); + if (instanceFields.size() == 0) { + return 8; + } + + int lastItemIndex = instanceFields.size()-1; + int fieldOffset = instanceFields.keyAt(lastItemIndex); + FieldReference lastField = instanceFields.valueAt(lastItemIndex); + + switch (lastField.getType().charAt(0)) { + case 'J': + case 'D': + return fieldOffset + 8; + default: + return fieldOffset + 4; + } + } + + @Nonnull List getVtable() { + return vtableSupplier.get(); + } + + //TODO: check the case when we have a package private method that overrides an interface method + @Nonnull private final Supplier> vtableSupplier = Suppliers.memoize(new Supplier>() { + @Override public List get() { + List vtable = Lists.newArrayList(); + + //copy the virtual methods from the superclass + String superclassType; + try { + superclassType = getSuperclass(); + } catch (UnresolvedClassException ex) { + vtable.addAll(((ClassProto)classPath.getClass("Ljava/lang/Object;")).getVtable()); + vtableFullyResolved = false; + return vtable; + } + + if (superclassType != null) { + ClassProto superclass = (ClassProto) classPath.getClass(superclassType); + vtable.addAll(superclass.getVtable()); + + // if the superclass's vtable wasn't fully resolved, then we can't know where the new methods added by this + // class should start, so we just propagate what we can from the parent and hope for the best. + if (!superclass.vtableFullyResolved) { + vtableFullyResolved = false; + return vtable; + } + } + + //iterate over the virtual methods in the current class, and only add them when we don't already have the + //method (i.e. if it was implemented by the superclass) + if (!isInterface()) { + addToVtable(getClassDef().getVirtualMethods(), vtable, true); + + // assume that interface method is implemented in the current class, when adding it to vtable + // otherwise it looks like that method is invoked on an interface, which fails Dalvik's optimization checks + for (ClassDef interfaceDef: getDirectInterfaces()) { + List interfaceMethods = Lists.newArrayList(); + for (Method interfaceMethod: interfaceDef.getVirtualMethods()) { + ImmutableMethod method = new ImmutableMethod( + type, + interfaceMethod.getName(), + interfaceMethod.getParameters(), + interfaceMethod.getReturnType(), + interfaceMethod.getAccessFlags(), + interfaceMethod.getAnnotations(), + interfaceMethod.getImplementation()); + interfaceMethods.add(method); + } + addToVtable(interfaceMethods, vtable, false); + } + } + return vtable; + } + + private void addToVtable(@Nonnull Iterable localMethods, + @Nonnull List vtable, boolean replaceExisting) { + List methods = Lists.newArrayList(localMethods); + Collections.sort(methods); + + outer: for (Method virtualMethod: methods) { + for (int i=0; i lines = new ArrayList(); - BufferedReader br = new BufferedReader(fr); + BufferedReader br = new BufferedReader(reader); try { String line = br.readLine(); @@ -66,21 +71,24 @@ public class CustomInlineMethodResolver extends InlineMethodResolver { line = br.readLine(); } } catch (IOException ex) { - throw new RuntimeException("Error while reading file: " + inlineTable, ex); + throw new RuntimeException("Error while parsing inline table", ex); } - inlineMethods = new DeodexUtil.InlineMethod[lines.size()]; + inlineMethods = new Method[lines.size()]; for (int i=0; i= inlineMethods.length) { @@ -91,7 +99,8 @@ public class CustomInlineMethodResolver extends InlineMethodResolver { private static final Pattern longMethodPattern = Pattern.compile("(L[^;]+;)->([^(]+)\\(([^)]*)\\)(.+)"); - private DeodexUtil.InlineMethod parseAndResolveInlineMethod(String inlineMethod) { + @Nonnull + private Method parseAndResolveInlineMethod(@Nonnull String inlineMethod) { Matcher m = longMethodPattern.matcher(inlineMethod); if (!m.matches()) { assert false; @@ -100,16 +109,30 @@ public class CustomInlineMethodResolver extends InlineMethodResolver { String className = m.group(1); String methodName = m.group(2); - String methodParams = m.group(3); + Iterable methodParams = ParamUtil.parseParamString(m.group(3)); String methodRet = m.group(4); + ImmutableMethodReference methodRef = new ImmutableMethodReference(className, methodName, methodParams, + methodRet); - ClassPath.ClassDef classDef = ClassPath.getClassDef(className, false); - int methodType = classDef.getMethodType(String.format("%s(%s)%s", methodName, methodParams, methodRet)); + int accessFlags = 0; - if (methodType == -1) { + boolean resolved = false; + TypeProto typeProto = classPath.getClass(className); + if (typeProto instanceof ClassProto) { + ClassDef classDef = ((ClassProto)typeProto).getClassDef(); + for (Method method: classDef.getMethods()) { + if (method.equals(methodRef)) { + resolved = true; + accessFlags = method.getAccessFlags(); + break; + } + } + } + + if (!resolved) { throw new RuntimeException("Cannot resolve inline method: " + inlineMethod); } - return new DeodexUtil.InlineMethod(methodType, className, methodName, methodParams, methodRet); + return new ImmutableMethod(className, methodName, methodParams, methodRet, accessFlags, null, null); } } diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java new file mode 100644 index 00000000..d57ce961 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpFields.java @@ -0,0 +1,170 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import org.apache.commons.cli.*; +import org.jf.dexlib2.DexFileFactory; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.Field; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.util.ConsoleUtil; +import org.jf.util.SparseArray; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +public class DumpFields { + private static final Options options; + + static { + options = new Options(); + buildOptions(); + } + + public static void main(String[] args) { + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + usage(); + return; + } + + String[] remainingArgs = commandLine.getArgs(); + + Option[] parsedOptions = commandLine.getOptions(); + ArrayList bootClassPathDirs = Lists.newArrayList(); + String outFile = "fields.txt"; + int apiLevel = 15; + + for (int i=0; i bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar"); + ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel); + FileOutputStream outStream = new FileOutputStream(outFile); + + for (ClassDef classDef: dexFile.getClasses()) { + ClassProto classProto = (ClassProto) classPath.getClass(classDef); + SparseArray fields = classProto.getInstanceFields(); + String className = "Class " + classDef.getType() + " : " + fields.size() + " instance fields\n"; + outStream.write(className.getBytes()); + for (int i=0;i"); + } + + private static void buildOptions() { + Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") + .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + + "directory") + .hasArg() + .withArgName("DIR") + .create("d"); + + Option outputFileOption = OptionBuilder.withLongOpt("out-file") + .withDescription("output file") + .hasArg() + .withArgName("FILE") + .create("o"); + + Option apiLevelOption = OptionBuilder.withLongOpt("api-level") + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 15 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); + + options.addOption(classPathDirOption); + options.addOption(outputFileOption); + options.addOption(apiLevelOption); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java new file mode 100644 index 00000000..0d7656c3 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/DumpVtables.java @@ -0,0 +1,174 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import org.apache.commons.cli.*; +import org.jf.dexlib2.DexFileFactory; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.Method; +import org.jf.util.ConsoleUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class DumpVtables { + private static final Options options; + + static { + options = new Options(); + buildOptions(); + } + + public static void main(String[] args) { + CommandLineParser parser = new PosixParser(); + CommandLine commandLine; + + try { + commandLine = parser.parse(options, args); + } catch (ParseException ex) { + usage(); + return; + } + + String[] remainingArgs = commandLine.getArgs(); + + Option[] parsedOptions = commandLine.getOptions(); + ArrayList bootClassPathDirs = Lists.newArrayList(); + String outFile = "vtables.txt"; + int apiLevel = 15; + + for (int i=0; i bootClassPaths = Splitter.on(":").split("core.jar:ext.jar:framework.jar:android.policy.jar:services.jar"); + ClassPath classPath = ClassPath.fromClassPath(bootClassPathDirs, bootClassPaths, dexFile, apiLevel); + FileOutputStream outStream = new FileOutputStream(outFile); + + for (ClassDef classDef: dexFile.getClasses()) { + ClassProto classProto = (ClassProto) classPath.getClass(classDef); + List methods = classProto.getVtable(); + String className = "Class " + classDef.getType() + " extends " + classDef.getSuperclass() + " : " + methods.size() + " methods\n"; + outStream.write(className.getBytes()); + for (int i=0;i" + method.getName() + "("; + for (CharSequence parameter: method.getParameterTypes()) { + methodString += parameter; + } + methodString += ")" + method.getReturnType() + "\n"; + outStream.write(methodString.getBytes()); + } + outStream.write("\n".getBytes()); + } + outStream.close(); + } catch (IOException ex) { + System.out.println("IOException thrown when trying to open a dex file or write out vtables: " + ex); + } + + } + + /** + * Prints the usage message. + */ + private static void usage() { + int consoleWidth = ConsoleUtil.getConsoleWidth(); + if (consoleWidth <= 0) { + consoleWidth = 80; + } + + System.out.println("java -cp baksmali.jar org.jf.dexlib2.analysis.DumpVtables -d path/to/framework/jar/files "); + } + + private static void buildOptions() { + Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir") + .withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " + + "directory") + .hasArg() + .withArgName("DIR") + .create("d"); + + Option outputFileOption = OptionBuilder.withLongOpt("out-file") + .withDescription("output file") + .hasArg() + .withArgName("FILE") + .create("o"); + + Option apiLevelOption = OptionBuilder.withLongOpt("api-level") + .withDescription("The numeric api-level of the file being disassembled. If not " + + "specified, it defaults to 15 (ICS).") + .hasArg() + .withArgName("API_LEVEL") + .create("a"); + + options.addOption(classPathDirOption); + options.addOption(outputFileOption); + options.addOption(apiLevelOption); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/InlineMethodResolver.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/InlineMethodResolver.java new file mode 100644 index 00000000..4b2e7e64 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/InlineMethodResolver.java @@ -0,0 +1,201 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.instruction.InlineIndexInstruction; +import org.jf.dexlib2.iface.instruction.VariableRegisterInstruction; +import org.jf.dexlib2.immutable.ImmutableMethod; +import org.jf.dexlib2.immutable.ImmutableMethodParameter; +import org.jf.dexlib2.immutable.util.ParamUtil; + +import javax.annotation.Nonnull; + +public abstract class InlineMethodResolver { + // These are the possible values for the accessFlag field on a resolved inline method + // We can't use, e.g. AccessFlags.STATIC.value, because we need them to be a constant in order to use them as cases + // in switch statements + public static final int STATIC = 0x8; // AccessFlags.STATIC.value; + public static final int VIRTUAL = 0x1; // AccessFlags.PUBLIC.value; + public static final int DIRECT = 0x2; // AccessFlags.PRIVATE.value; + + @Nonnull + public static InlineMethodResolver createInlineMethodResolver(int odexVersion) { + if (odexVersion == 35) { + return new InlineMethodResolver_version35(); + } else if (odexVersion == 36) { + return new InlineMethodResolver_version36(); + } else { + throw new RuntimeException(String.format("odex version %d is not supported yet", odexVersion)); + } + } + + protected InlineMethodResolver() { + } + + @Nonnull + private static Method inlineMethod(int accessFlags, @Nonnull String cls, @Nonnull String name, + @Nonnull String params, @Nonnull String returnType) { + ImmutableList paramList = ImmutableList.copyOf(ParamUtil.parseParamString(params)); + return new ImmutableMethod(cls, name, paramList, returnType, accessFlags, null, null); + } + + @Nonnull public abstract Method resolveExecuteInline(@Nonnull AnalyzedInstruction instruction); + + private static class InlineMethodResolver_version35 extends InlineMethodResolver + { + private final Method[] inlineMethods; + + public InlineMethodResolver_version35() { + inlineMethods = new Method[] { + inlineMethod(STATIC, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"), + inlineMethod(VIRTUAL, "Ljava/lang/String;", "charAt", "I", "C"), + inlineMethod(VIRTUAL, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"), + inlineMethod(VIRTUAL, "Ljava/lang/String;", "equals", "Ljava/lang/Object;", "Z"), + inlineMethod(VIRTUAL, "Ljava/lang/String;", "length", "", "I"), + inlineMethod(STATIC, "Ljava/lang/Math;", "abs", "I", "I"), + inlineMethod(STATIC, "Ljava/lang/Math;", "abs", "J", "J"), + inlineMethod(STATIC, "Ljava/lang/Math;", "abs", "F", "F"), + inlineMethod(STATIC, "Ljava/lang/Math;", "abs", "D", "D"), + inlineMethod(STATIC, "Ljava/lang/Math;", "min", "II", "I"), + inlineMethod(STATIC, "Ljava/lang/Math;", "max", "II", "I"), + inlineMethod(STATIC, "Ljava/lang/Math;", "sqrt", "D", "D"), + inlineMethod(STATIC, "Ljava/lang/Math;", "cos", "D", "D"), + inlineMethod(STATIC, "Ljava/lang/Math;", "sin", "D", "D") + }; + } + + @Override + @Nonnull + public Method resolveExecuteInline(@Nonnull AnalyzedInstruction analyzedInstruction) { + InlineIndexInstruction instruction = (InlineIndexInstruction)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 Method[] inlineMethods; + private final Method indexOfIMethod; + private final Method indexOfIIMethod; + private final Method fastIndexOfMethod; + private final Method isEmptyMethod; + + public InlineMethodResolver_version36() { + //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 = inlineMethod(VIRTUAL, "Ljava/lang/String;", "indexOf", "I", "I"); + indexOfIIMethod = inlineMethod(VIRTUAL, "Ljava/lang/String;", "indexOf", "II", "I"); + + //gingerbread + fastIndexOfMethod = inlineMethod(DIRECT, "Ljava/lang/String;", "fastIndexOf", "II", "I"); + isEmptyMethod = inlineMethod(VIRTUAL, "Ljava/lang/String;", "isEmpty", "", "Z"); + + inlineMethods = new Method[] { + inlineMethod(STATIC, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "", "V"), + inlineMethod(VIRTUAL, "Ljava/lang/String;", "charAt", "I", "C"), + inlineMethod(VIRTUAL, "Ljava/lang/String;", "compareTo", "Ljava/lang/String;", "I"), + 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, + inlineMethod(VIRTUAL, "Ljava/lang/String;", "length", "", "I"), + inlineMethod(STATIC, "Ljava/lang/Math;", "abs", "I", "I"), + inlineMethod(STATIC, "Ljava/lang/Math;", "abs", "J", "J"), + inlineMethod(STATIC, "Ljava/lang/Math;", "abs", "F", "F"), + inlineMethod(STATIC, "Ljava/lang/Math;", "abs", "D", "D"), + inlineMethod(STATIC, "Ljava/lang/Math;", "min", "II", "I"), + inlineMethod(STATIC, "Ljava/lang/Math;", "max", "II", "I"), + inlineMethod(STATIC, "Ljava/lang/Math;", "sqrt", "D", "D"), + inlineMethod(STATIC, "Ljava/lang/Math;", "cos", "D", "D"), + inlineMethod(STATIC, "Ljava/lang/Math;", "sin", "D", "D"), + inlineMethod(STATIC, "Ljava/lang/Float;", "floatToIntBits", "F", "I"), + inlineMethod(STATIC, "Ljava/lang/Float;", "floatToRawIntBits", "F", "I"), + inlineMethod(STATIC, "Ljava/lang/Float;", "intBitsToFloat", "I", "F"), + inlineMethod(STATIC, "Ljava/lang/Double;", "doubleToLongBits", "D", "J"), + inlineMethod(STATIC, "Ljava/lang/Double;", "doubleToRawLongBits", "D", "J"), + inlineMethod(STATIC, "Ljava/lang/Double;", "longBitsToDouble", "J", "D"), + inlineMethod(STATIC, "Ljava/lang/StrictMath;", "abs", "I", "I"), + inlineMethod(STATIC, "Ljava/lang/StrictMath;", "abs", "J", "J"), + inlineMethod(STATIC, "Ljava/lang/StrictMath;", "abs", "F", "F"), + inlineMethod(STATIC, "Ljava/lang/StrictMath;", "abs", "D", "D"), + inlineMethod(STATIC, "Ljava/lang/StrictMath;", "min", "II", "I"), + inlineMethod(STATIC, "Ljava/lang/StrictMath;", "max", "II", "I"), + inlineMethod(STATIC, "Ljava/lang/StrictMath;", "sqrt", "D", "D"), + }; + } + + @Override + @Nonnull + public Method resolveExecuteInline(@Nonnull AnalyzedInstruction analyzedInstruction) { + InlineIndexInstruction instruction = (InlineIndexInstruction)analyzedInstruction.instruction; + int inlineIndex = instruction.getInlineIndex(); + + if (inlineIndex < 0 || inlineIndex >= inlineMethods.length) { + throw new RuntimeException("Invalid method index: " + inlineIndex); + } + + if (inlineIndex == 4) { + int parameterCount = ((VariableRegisterInstruction)instruction).getRegisterCount(); + 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 = ((VariableRegisterInstruction)instruction).getRegisterCount(); + 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]; + } + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java new file mode 100644 index 00000000..fc2d2b9f --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java @@ -0,0 +1,1770 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import org.jf.dexlib2.AccessFlags; +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.*; +import org.jf.dexlib2.iface.instruction.*; +import org.jf.dexlib2.iface.instruction.formats.*; +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.Reference; +import org.jf.dexlib2.iface.reference.TypeReference; +import org.jf.dexlib2.immutable.instruction.*; +import org.jf.dexlib2.immutable.reference.ImmutableFieldReference; +import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; +import org.jf.dexlib2.util.MethodUtil; +import org.jf.dexlib2.util.ReferenceUtil; +import org.jf.dexlib2.util.TypeUtils; +import org.jf.util.BitSetUtils; +import org.jf.util.ExceptionWithContext; +import org.jf.util.SparseArray; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.BitSet; +import java.util.List; + +/** + * The MethodAnalyzer performs several functions. It "analyzes" the instructions and infers the register types + * for each register, it can deodex odexed instructions, and it can verify the bytecode. The analysis and verification + * are done in two separate passes, because the analysis has to process instructions multiple times in some cases, and + * there's no need to perform the verification multiple times, so we wait until the method is fully analyzed and then + * verify it. + * + * Before calling the analyze() method, you must have initialized the ClassPath by calling + * ClassPath.InitializeClassPath + */ +public class MethodAnalyzer { + @Nonnull private final Method method; + @Nonnull private final MethodImplementation methodImpl; + + private final int paramRegisterCount; + + @Nonnull private final ClassPath classPath; + @Nullable private final InlineMethodResolver inlineResolver; + + // This contains all the AnalyzedInstruction instances, keyed by the code unit address of the instruction + @Nonnull private final SparseArray analyzedInstructions = + new SparseArray(0); + + // Which instructions have been analyzed, keyed by instruction index + @Nonnull private final BitSet analyzedState; + + @Nullable private AnalysisException analysisException = null; + + //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the + //register types for this instruction to the parameter types, in order to have them propagate to all of its + //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first + //instruction, etc. + private final AnalyzedInstruction startOfMethod; + + public MethodAnalyzer(@Nonnull ClassPath classPath, @Nonnull Method method, + @Nullable InlineMethodResolver inlineResolver) { + this.classPath = classPath; + this.inlineResolver = inlineResolver; + + this.method = method; + + MethodImplementation methodImpl = method.getImplementation(); + if (methodImpl == null) { + throw new IllegalArgumentException("The method has no implementation"); + } + + this.methodImpl = methodImpl; + + //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't + //have to handle the case this special case of instruction being null, in the main class + startOfMethod = new AnalyzedInstruction(null, -1, methodImpl.getRegisterCount()) { + public boolean setsRegister() { + return false; + } + + @Override + public boolean setsWideRegister() { + return false; + } + + @Override + public boolean setsRegister(int registerNumber) { + return false; + } + + @Override + public int getDestinationRegister() { + assert false; + return -1; + } + }; + + buildInstructionList(); + + analyzedState = new BitSet(analyzedInstructions.size()); + paramRegisterCount = MethodUtil.getParameterRegisterCount(method); + analyze(); + } + + private void analyze() { + Method method = this.method; + MethodImplementation methodImpl = this.methodImpl; + + int totalRegisters = methodImpl.getRegisterCount(); + int parameterRegisters = paramRegisterCount; + + int nonParameterRegisters = totalRegisters - parameterRegisters; + + //if this isn't a static method, determine which register is the "this" register and set the type to the + //current class + if (!MethodUtil.isStatic(method)) { + int thisRegister = totalRegisters - parameterRegisters; + + //if this is a constructor, then set the "this" register to an uninitialized reference of the current class + if (MethodUtil.isConstructor(method)) { + setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, + RegisterType.getRegisterType(RegisterType.UNINIT_THIS, + classPath.getClass(method.getDefiningClass()))); + } else { + setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister, + RegisterType.getRegisterType(RegisterType.REFERENCE, + classPath.getClass(method.getDefiningClass()))); + } + + propagateParameterTypes(totalRegisters-parameterRegisters+1); + } else { + propagateParameterTypes(totalRegisters-parameterRegisters); + } + + RegisterType uninit = RegisterType.getRegisterType(RegisterType.UNINIT, null); + for (int i=0; i=0; i=instructionsToAnalyze.nextSetBit(i+1)) { + instructionsToAnalyze.clear(i); + if (analyzedState.get(i)) { + continue; + } + AnalyzedInstruction instructionToAnalyze = analyzedInstructions.valueAt(i); + try { + if (instructionToAnalyze.originalInstruction.getOpcode().odexOnly()) { + //if we had deodexed an odex instruction in a previous pass, we might have more specific + //register information now, so let's restore the original odexed instruction and + //re-deodex it + instructionToAnalyze.restoreOdexedInstruction(); + } + + if (!analyzeInstruction(instructionToAnalyze)) { + undeodexedInstructions.set(i); + continue; + } else { + didSomething = true; + undeodexedInstructions.clear(i); + } + } catch (AnalysisException ex) { + this.analysisException = ex; + int codeAddress = getInstructionAddress(instructionToAnalyze); + ex.codeAddress = codeAddress; + ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.getOpcode().name)); + ex.addContext(String.format("code address: %d", codeAddress)); + ex.addContext(String.format("method: %s", ReferenceUtil.getReferenceString(method))); + break; + } + + analyzedState.set(instructionToAnalyze.getInstructionIndex()); + + for (AnalyzedInstruction successor: instructionToAnalyze.successors) { + instructionsToAnalyze.set(successor.getInstructionIndex()); + } + } + if (analysisException != null) { + break; + } + } + + if (!didSomething) { + break; + } + + if (!undeodexedInstructions.isEmpty()) { + for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) { + instructionsToAnalyze.set(i); + } + } + } while (true); + + //Now, go through and fix up any unresolvable odex instructions. These are usually odex instructions + //that operate on a null register, and thus always throw an NPE. They can also be any sort of odex instruction + //that occurs after an unresolvable odex instruction. We deodex if possible, or replace with an + //UnresolvableOdexInstruction + for (int i=0; i< analyzedInstructions.size(); i++) { + AnalyzedInstruction analyzedInstruction = analyzedInstructions.valueAt(i); + + Instruction instruction = analyzedInstruction.getInstruction(); + + if (instruction.getOpcode().odexOnly()) { + int objectRegisterNumber; + switch (instruction.getOpcode().format) { + case Format10x: + analyzeReturnVoidBarrier(analyzedInstruction, false); + continue; + case Format21c: + case Format22c: + analyzePutGetVolatile(analyzedInstruction, false); + continue; + case Format35c: + analyzeInvokeDirectEmpty(analyzedInstruction, false); + continue; + case Format3rc: + analyzeInvokeObjectInitRange(analyzedInstruction, false); + continue; + case Format22cs: + objectRegisterNumber = ((Instruction22cs)instruction).getRegisterB(); + break; + case Format35mi: + case Format35ms: + objectRegisterNumber = ((FiveRegisterInstruction)instruction).getRegisterC(); + break; + case Format3rmi: + case Format3rms: + objectRegisterNumber = ((RegisterRangeInstruction)instruction).getStartRegister(); + break; + default: + continue; + } + + analyzedInstruction.setDeodexedInstruction( + new UnresolvedOdexInstruction(instruction, objectRegisterNumber)); + } + } + } + + private void propagateParameterTypes(int parameterStartRegister) { + int i=0; + for (MethodParameter parameter: method.getParameters()) { + if (TypeUtils.isWideType(parameter)) { + setPostRegisterTypeAndPropagateChanges(startOfMethod, parameterStartRegister + i++, + RegisterType.getWideRegisterType(parameter, true)); + setPostRegisterTypeAndPropagateChanges(startOfMethod, parameterStartRegister + i++, + RegisterType.getWideRegisterType(parameter, false)); + } else { + setPostRegisterTypeAndPropagateChanges(startOfMethod, parameterStartRegister + i++, + RegisterType.getRegisterType(classPath, parameter)); + } + } + } + + public List getAnalyzedInstructions() { + return analyzedInstructions.getValues(); + } + + public List getInstructions() { + return Lists.transform(analyzedInstructions.getValues(), new Function() { + @Nullable @Override public Instruction apply(@Nullable AnalyzedInstruction input) { + if (input == null) { + return null; + } + return input.instruction; + } + }); + } + + @Nullable + public AnalysisException getAnalysisException() { + return analysisException; + } + + public int getParamRegisterCount() { + return paramRegisterCount; + } + + public int getInstructionAddress(@Nonnull AnalyzedInstruction instruction) { + return analyzedInstructions.keyAt(instruction.instructionIndex); + } + + private void setDestinationRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction, + @Nonnull RegisterType registerType) { + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(), + registerType); + } + + private void setPostRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction, + int registerNumber, @Nonnull RegisterType registerType) { + + BitSet changedInstructions = new BitSet(analyzedInstructions.size()); + + if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) { + return; + } + + propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions); + + //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction + //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on + //the next iteration of the while loop. + //This could also be done recursively, but in large methods it would likely cause very deep recursion, + //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly + //annoying. + while (!changedInstructions.isEmpty()) { + for (int instructionIndex=changedInstructions.nextSetBit(0); + instructionIndex>=0; + instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) { + + changedInstructions.clear(instructionIndex); + + propagateRegisterToSuccessors(analyzedInstructions.valueAt(instructionIndex), registerNumber, + changedInstructions); + } + } + + if (registerType.category == RegisterType.LONG_LO) { + checkWidePair(registerNumber, analyzedInstruction); + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, RegisterType.LONG_HI_TYPE); + } else if (registerType.category == RegisterType.DOUBLE_LO) { + checkWidePair(registerNumber, analyzedInstruction); + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, RegisterType.DOUBLE_HI_TYPE); + } + } + + private void propagateRegisterToSuccessors(@Nonnull AnalyzedInstruction instruction, int registerNumber, + @Nonnull BitSet changedInstructions) { + RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber); + for (AnalyzedInstruction successor: instruction.successors) { + if (successor.mergeRegister(registerNumber, postRegisterType, analyzedState)) { + changedInstructions.set(successor.instructionIndex); + } + } + } + + private void buildInstructionList() { + int registerCount = methodImpl.getRegisterCount(); + + ImmutableList instructions = ImmutableList.copyOf(methodImpl.getInstructions()); + + analyzedInstructions.ensureCapacity(instructions.size()); + + //first, create all the instructions and populate the instructionAddresses array + int currentCodeAddress = 0; + for (int i=0; i> tries = methodImpl.getTryBlocks(); + int triesIndex = 0; + TryBlock currentTry = null; + AnalyzedInstruction[] currentExceptionHandlers = null; + AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[instructions.size()][]; + + if (tries != null) { + for (int i=0; i< analyzedInstructions.size(); i++) { + AnalyzedInstruction instruction = analyzedInstructions.valueAt(i); + Opcode instructionOpcode = instruction.instruction.getOpcode(); + currentCodeAddress = getInstructionAddress(instruction); + + //check if we have gone past the end of the current try + if (currentTry != null) { + if (currentTry.getStartCodeAddress() + currentTry.getCodeUnitCount() <= currentCodeAddress) { + currentTry = null; + triesIndex++; + } + } + + //check if the next try is applicable yet + if (currentTry == null && triesIndex < tries.size()) { + TryBlock tryBlock = tries.get(triesIndex); + if (tryBlock.getStartCodeAddress() <= currentCodeAddress) { + assert(tryBlock.getStartCodeAddress() + tryBlock.getCodeUnitCount() > currentCodeAddress); + + currentTry = tryBlock; + + currentExceptionHandlers = buildExceptionHandlerArray(tryBlock); + } + } + + //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers + //for the current instruction + if (currentTry != null && instructionOpcode.canThrow()) { + exceptionHandlers[i] = currentExceptionHandlers; + } + } + } + + //finally, populate the successors and predecessors for each instruction. We start at the fake "StartOfMethod" + //instruction and follow the execution path. Any unreachable code won't have any predecessors or successors, + //and no reachable code will have an unreachable predessor or successor + assert analyzedInstructions.size() > 0; + BitSet instructionsToProcess = new BitSet(instructions.size()); + + addPredecessorSuccessor(startOfMethod, analyzedInstructions.valueAt(0), exceptionHandlers, instructionsToProcess); + while (!instructionsToProcess.isEmpty()) { + int currentInstructionIndex = instructionsToProcess.nextSetBit(0); + instructionsToProcess.clear(currentInstructionIndex); + + AnalyzedInstruction instruction = analyzedInstructions.valueAt(currentInstructionIndex); + Opcode instructionOpcode = instruction.instruction.getOpcode(); + int instructionCodeAddress = getInstructionAddress(instruction); + + if (instruction.instruction.getOpcode().canContinue()) { + if (currentInstructionIndex == analyzedInstructions.size() - 1) { + throw new AnalysisException("Execution can continue past the last instruction"); + } + + AnalyzedInstruction nextInstruction = analyzedInstructions.valueAt(currentInstructionIndex+1); + addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess); + } + + if (instruction.instruction instanceof OffsetInstruction) { + OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction; + + if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) { + SwitchPayload switchPayload = (SwitchPayload)analyzedInstructions.get(instructionCodeAddress + + offsetInstruction.getCodeOffset()).instruction; + for (SwitchElement switchElement: switchPayload.getSwitchElements()) { + AnalyzedInstruction targetInstruction = analyzedInstructions.get(instructionCodeAddress + + switchElement.getOffset()); + + addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, + instructionsToProcess); + } + } else if (instructionOpcode != Opcode.FILL_ARRAY_DATA) { + int targetAddressOffset = offsetInstruction.getCodeOffset(); + AnalyzedInstruction targetInstruction = analyzedInstructions.get(instructionCodeAddress + + targetAddressOffset); + addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess); + } + } + } + } + + private void addPredecessorSuccessor(@Nonnull AnalyzedInstruction predecessor, + @Nonnull AnalyzedInstruction successor, + @Nonnull AnalyzedInstruction[][] exceptionHandlers, + @Nonnull BitSet instructionsToProcess) { + addPredecessorSuccessor(predecessor, successor, exceptionHandlers, instructionsToProcess, false); + } + + private void addPredecessorSuccessor(@Nonnull AnalyzedInstruction predecessor, + @Nonnull AnalyzedInstruction successor, + @Nonnull AnalyzedInstruction[][] exceptionHandlers, + @Nonnull BitSet instructionsToProcess, boolean allowMoveException) { + + if (!allowMoveException && successor.instruction.getOpcode() == Opcode.MOVE_EXCEPTION) { + throw new AnalysisException("Execution can pass from the " + predecessor.instruction.getOpcode().name + + " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) + + " to the move-exception instruction at address 0x" + + Integer.toHexString(getInstructionAddress(successor))); + } + + if (!successor.addPredecessor(predecessor)) { + return; + } + + predecessor.addSuccessor(successor); + instructionsToProcess.set(successor.getInstructionIndex()); + + + //if the successor can throw an instruction, then we need to add the exception handlers as additional + //successors to the predecessor (and then apply this same logic recursively if needed) + //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually + //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other + //instruction. But since it doesn't modify any registers, we can treat it like any other instruction. + AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex]; + if (exceptionHandlersForSuccessor != null) { + //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction + //can throw an exception + assert successor.instruction.getOpcode().canThrow(); + + for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) { + addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true); + } + } + } + + @Nonnull + private AnalyzedInstruction[] buildExceptionHandlerArray(@Nonnull TryBlock tryBlock) { + List exceptionHandlers = tryBlock.getExceptionHandlers(); + + AnalyzedInstruction[] handlerInstructions = new AnalyzedInstruction[exceptionHandlers.size()]; + for (int i=0; i tryBlock: methodImpl.getTryBlocks()) { + for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) { + + if (handler.getHandlerCodeAddress() == instructionAddress) { + String type = handler.getExceptionType(); + if (type == null) { + exceptionType = RegisterType.getRegisterType(RegisterType.REFERENCE, + classPath.getClass("Ljava/lang/Throwable;")); + } else { + exceptionType = RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(type)) + .merge(exceptionType); + } + } + } + } + + if (exceptionType.category == RegisterType.UNKNOWN) { + throw new AnalysisException("move-exception must be the first instruction in an exception handler block"); + } + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType); + } + + private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction) { + analyzeReturnVoidBarrier(analyzedInstruction, true); + } + + private void analyzeReturnVoidBarrier(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { + Instruction10x deodexedInstruction = new ImmutableInstruction10x(Opcode.RETURN_VOID); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + if (analyzeResult) { + analyzeInstruction(analyzedInstruction); + } + } + + private void analyzeConst(@Nonnull AnalyzedInstruction analyzedInstruction) { + NarrowLiteralInstruction instruction = (NarrowLiteralInstruction)analyzedInstruction.instruction; + + //we assume that the literal value is a valid value for the given instruction type, because it's impossible + //to store an invalid literal with the instruction. so we don't need to check the type of the literal + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterTypeForLiteral(instruction.getNarrowLiteral())); + } + + private void analyzeWideConst(@Nonnull AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.LONG_LO_TYPE); + } + + private void analyzeConstString(@Nonnull AnalyzedInstruction analyzedInstruction) { + TypeProto stringClass = classPath.getClass("Ljava/lang/String;"); + RegisterType stringType = RegisterType.getRegisterType(RegisterType.REFERENCE, stringClass); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType); + } + + private void analyzeConstClass(@Nonnull AnalyzedInstruction analyzedInstruction) { + TypeProto classClass = classPath.getClass("Ljava/lang/Class;"); + RegisterType classType = RegisterType.getRegisterType(RegisterType.REFERENCE, classClass); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType); + } + + private void analyzeCheckCast(@Nonnull AnalyzedInstruction analyzedInstruction) { + ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction; + TypeReference reference = (TypeReference)instruction.getReference(); + RegisterType castRegisterType = RegisterType.getRegisterType(classPath, reference); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType); + } + + private void analyzeInstanceOf(@Nonnull AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.BOOLEAN_TYPE); + } + + private void analyzeArrayLength(@Nonnull AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.INTEGER_TYPE); + } + + private void analyzeNewInstance(@Nonnull AnalyzedInstruction analyzedInstruction) { + ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction; + + int register = ((OneRegisterInstruction)analyzedInstruction.instruction).getRegisterA(); + RegisterType destRegisterType = analyzedInstruction.getPostInstructionRegisterType(register); + if (destRegisterType.category != RegisterType.UNKNOWN) { + //the post-instruction destination register will only be set if we have already analyzed this instruction + //at least once. If this is the case, then the uninit reference has already been propagated to all + //successors and nothing else needs to be done. + assert destRegisterType.category == RegisterType.UNINIT_REF; + return; + } + + TypeReference typeReference = (TypeReference)instruction.getReference(); + + RegisterType classType = RegisterType.getRegisterType(classPath, typeReference); + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.UNINIT_REF, classType.type)); + } + + private void analyzeNewArray(@Nonnull AnalyzedInstruction analyzedInstruction) { + ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction; + + TypeReference type = (TypeReference)instruction.getReference(); + if (type.getType().charAt(0) != '[') { + throw new AnalysisException("new-array used with non-array type"); + } + + RegisterType arrayType = RegisterType.getRegisterType(classPath, type); + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType); + } + + private void analyzeFloatWideCmp(@Nonnull AnalyzedInstruction analyzedInstruction) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.BYTE_TYPE); + } + + private void analyze32BitPrimitiveAget(@Nonnull AnalyzedInstruction analyzedInstruction, + @Nonnull RegisterType registerType) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, registerType); + } + + private void analyzeAgetWide(@Nonnull AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + if (! ClassPath.dontLoadClassPath && arrayRegisterType.category != RegisterType.NULL) { + if (arrayRegisterType.category != RegisterType.REFERENCE || + !(arrayRegisterType.type instanceof ArrayProto)) { + throw new AnalysisException("aget-wide used with non-array register: %s", arrayRegisterType.toString()); + } + ArrayProto arrayProto = (ArrayProto)arrayRegisterType.type; + + if (arrayProto.dimensions != 1) { + throw new AnalysisException("aget-wide used with multi-dimensional array: %s", + arrayRegisterType.toString()); + } + + char arrayBaseType = arrayProto.getElementType().charAt(0); + if (arrayBaseType == 'J') { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.LONG_LO_TYPE); + } else if (arrayBaseType == 'D') { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE); + } else { + throw new AnalysisException("aget-wide used with narrow array: %s", arrayRegisterType); + } + } else { + // If the array register is null, we can assume that the destination register was meant to be a wide type. + // This is the same behavior as dalvik's verifier + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.LONG_LO_TYPE); + } + } + + private void analyzeAgetObject(@Nonnull AnalyzedInstruction analyzedInstruction) { + ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction; + + RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB()); + if (! ClassPath.dontLoadClassPath && arrayRegisterType.category != RegisterType.NULL) { + if (arrayRegisterType.category != RegisterType.REFERENCE || + !(arrayRegisterType.type instanceof ArrayProto)) { + throw new AnalysisException("aget-object used with non-array register: %s", + arrayRegisterType.toString()); + } + + ArrayProto arrayProto = (ArrayProto)arrayRegisterType.type; + + String elementType = arrayProto.getImmediateElementType(); + + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, + RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(elementType))); + } else { + // If the array register is null, we can assume that the destination register was meant to be a reference + // type, so we set the destination to NULL. This is the same behavior as dalvik's verifier + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.NULL_TYPE); + } + } + + private void analyze32BitPrimitiveIgetSget(@Nonnull AnalyzedInstruction analyzedInstruction, + @Nonnull RegisterType registerType) { + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, registerType); + } + + private void analyzeIgetSgetWideObject(@Nonnull AnalyzedInstruction analyzedInstruction) { + ReferenceInstruction referenceInstruction = (ReferenceInstruction)analyzedInstruction.instruction; + + FieldReference fieldReference = (FieldReference)referenceInstruction.getReference(); + + RegisterType fieldType = RegisterType.getRegisterType(classPath, fieldReference.getType()); + setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType); + } + + private void analyzeInvokeDirect(@Nonnull AnalyzedInstruction analyzedInstruction) { + FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction; + analyzeInvokeDirectCommon(analyzedInstruction, instruction.getRegisterC()); + } + + private void analyzeInvokeDirectRange(@Nonnull AnalyzedInstruction analyzedInstruction) { + RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction; + analyzeInvokeDirectCommon(analyzedInstruction, instruction.getStartRegister()); + } + + private void analyzeInvokeDirectCommon(@Nonnull AnalyzedInstruction analyzedInstruction, int objectRegister) { + //the only time that an invoke instruction changes a register type is when using invoke-direct on a + //constructor () method, which changes the uninitialized reference (and any register that the same + //uninit reference has been copied to) to an initialized reference + + ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction; + + MethodReference methodReference = (MethodReference)instruction.getReference(); + + if (!methodReference.getName().equals("")) { + return; + } + + RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister); + + if (objectRegisterType.category != RegisterType.UNINIT_REF && + objectRegisterType.category != RegisterType.UNINIT_THIS) { + return; + } + + setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister, + RegisterType.getRegisterType(RegisterType.REFERENCE, objectRegisterType.type)); + + for (int i=0; i 24) { + return RegisterType.POS_BYTE_TYPE; + } + if (literalShift >= 16) { + return RegisterType.CHAR_TYPE; + } + } else { + if (literalShift >= 24) { + return RegisterType.BYTE_TYPE; + } + if (literalShift >= 16) { + return RegisterType.SHORT_TYPE; + } + } + break; + case RegisterType.SHORT: + if (signedShift && literalShift >= 8) { + return RegisterType.BYTE_TYPE; + } + break; + case RegisterType.POS_SHORT: + if (literalShift >= 8) { + return RegisterType.POS_BYTE_TYPE; + } + break; + case RegisterType.CHAR: + if (literalShift > 8) { + return RegisterType.POS_BYTE_TYPE; + } + break; + case RegisterType.BYTE: + break; + case RegisterType.POS_BYTE: + return RegisterType.POS_BYTE_TYPE; + case RegisterType.NULL: + case RegisterType.ONE: + case RegisterType.BOOLEAN: + return RegisterType.NULL_TYPE; + default: + assert false; + } + + return destRegisterType; + } + + + private void analyzeExecuteInline(@Nonnull AnalyzedInstruction analyzedInstruction) { + if (inlineResolver == null) { + throw new AnalysisException("Cannot analyze an odexed instruction unless we are deodexing"); + } + + Instruction35mi instruction = (Instruction35mi)analyzedInstruction.instruction; + Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction); + + Opcode deodexedOpcode; + int acccessFlags = resolvedMethod.getAccessFlags(); + if (AccessFlags.STATIC.isSet(acccessFlags)) { + deodexedOpcode = Opcode.INVOKE_STATIC; + } else if (AccessFlags.PRIVATE.isSet(acccessFlags)) { + deodexedOpcode = Opcode.INVOKE_DIRECT; + } else { + deodexedOpcode = Opcode.INVOKE_VIRTUAL; + } + + Instruction35c deodexedInstruction = new ImmutableInstruction35c(deodexedOpcode, instruction.getRegisterCount(), + instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(), + instruction.getRegisterF(), instruction.getRegisterG(), resolvedMethod); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + analyzeInstruction(analyzedInstruction); + } + + private void analyzeExecuteInlineRange(@Nonnull AnalyzedInstruction analyzedInstruction) { + if (inlineResolver == null) { + throw new AnalysisException("Cannot analyze an odexed instruction unless we are deodexing"); + } + + Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction; + Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction); + + Opcode deodexedOpcode; + int acccessFlags = resolvedMethod.getAccessFlags(); + if (AccessFlags.STATIC.isSet(acccessFlags)) { + deodexedOpcode = Opcode.INVOKE_STATIC_RANGE; + } else if (AccessFlags.PRIVATE.isSet(acccessFlags)) { + deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE; + } else { + deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE; + } + + Instruction3rc deodexedInstruction = new ImmutableInstruction3rc(deodexedOpcode, instruction.getStartRegister(), + instruction.getRegisterCount(), resolvedMethod); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + analyzeInstruction(analyzedInstruction); + } + + private void analyzeInvokeDirectEmpty(@Nonnull AnalyzedInstruction analyzedInstruction) { + analyzeInvokeDirectEmpty(analyzedInstruction, true); + } + + private void analyzeInvokeDirectEmpty(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { + Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction; + + Instruction35c deodexedInstruction = new ImmutableInstruction35c(Opcode.INVOKE_DIRECT, + instruction.getRegisterCount(), instruction.getRegisterC(), instruction.getRegisterD(), + instruction.getRegisterE(), instruction.getRegisterF(), instruction.getRegisterG(), + instruction.getReference()); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + if (analyzeResult) { + analyzeInstruction(analyzedInstruction); + } + } + + private void analyzeInvokeObjectInitRange(@Nonnull AnalyzedInstruction analyzedInstruction) { + analyzeInvokeObjectInitRange(analyzedInstruction, true); + } + + private void analyzeInvokeObjectInitRange(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { + Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction; + + Instruction deodexedInstruction; + + int startRegister = instruction.getStartRegister(); + int registerCount = instruction.getRegisterCount(); + if (registerCount == 1 && startRegister < 16) { + deodexedInstruction = new ImmutableInstruction35c(Opcode.INVOKE_DIRECT, + registerCount, startRegister, 0, 0, 0, 0, instruction.getReference()); + } else { + deodexedInstruction = new ImmutableInstruction3rc(Opcode.INVOKE_DIRECT_RANGE, + startRegister, registerCount, instruction.getReference()); + } + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + if (analyzeResult) { + analyzeInstruction(analyzedInstruction); + } + } + + private boolean analyzeIputIgetQuick(@Nonnull AnalyzedInstruction analyzedInstruction) { + Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction; + + int fieldOffset = instruction.getFieldOffset(); + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), + ReferenceOrUninitCategories); + + if (objectRegisterType.category == RegisterType.NULL) { + return false; + } + + TypeProto objectRegisterTypeProto = objectRegisterType.type; + assert objectRegisterTypeProto != null; + + TypeProto classTypeProto = classPath.getClass(objectRegisterTypeProto.getType()); + FieldReference resolvedField = classTypeProto.getFieldByOffset(fieldOffset); + + if (resolvedField == null) { + throw new AnalysisException("Could not resolve the field in class %s at offset %d", + objectRegisterType.type.getType(), fieldOffset); + } + + ClassDef thisClass = classPath.getClassDef(method.getDefiningClass()); + + if (!canAccessClass(thisClass, classPath.getClassDef(resolvedField.getDefiningClass()))) { + + // the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different + // than resolvedField.getDefiningClass()), and walk up the class hierarchy. + ClassDef fieldClass = classPath.getClassDef(objectRegisterTypeProto.getType()); + while (!canAccessClass(thisClass, fieldClass)) { + String superclass = fieldClass.getSuperclass(); + if (superclass == null) { + throw new ExceptionWithContext("Couldn't find accessible class while resolving field %s", + ReferenceUtil.getShortFieldDescriptor(resolvedField)); + } + + fieldClass = classPath.getClassDef(superclass); + } + + // fieldClass is now the first accessible class found. Now. we need to make sure that the field is + // actually valid for this class + resolvedField = classPath.getClass(fieldClass.getType()).getFieldByOffset(fieldOffset); + if (resolvedField == null) { + throw new ExceptionWithContext("Couldn't find accessible class while resolving field %s", + ReferenceUtil.getShortFieldDescriptor(resolvedField)); + } + resolvedField = new ImmutableFieldReference(fieldClass.getType(), resolvedField.getName(), + resolvedField.getType()); + } + + String fieldType = resolvedField.getType(); + + Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, + instruction.getOpcode()); + + Instruction22c deodexedInstruction = new ImmutableInstruction22c(opcode, (byte)instruction.getRegisterA(), + (byte)instruction.getRegisterB(), resolvedField); + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + analyzeInstruction(analyzedInstruction); + + return true; + } + + private boolean analyzeInvokeVirtualQuick(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isSuper, + boolean isRange) { + int methodIndex; + int objectRegister; + + if (isRange) { + Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; + methodIndex = instruction.getVtableIndex(); + objectRegister = instruction.getStartRegister(); + } else { + Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; + methodIndex = instruction.getVtableIndex(); + objectRegister = instruction.getRegisterC(); + } + + RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister, + ReferenceOrUninitCategories); + TypeProto objectRegisterTypeProto = objectRegisterType.type; + + if (objectRegisterType.category == RegisterType.NULL) { + return false; + } + + assert objectRegisterTypeProto != null; + + MethodReference resolvedMethod; + if (isSuper) { + // invoke-super is only used for the same class that we're currently in + TypeProto typeProto = classPath.getClass(method.getDefiningClass()); + TypeProto superType; + + String superclassType = typeProto.getSuperclass(); + if (superclassType != null) { + superType = classPath.getClass(superclassType); + } else { + // This is either java.lang.Object, or an UnknownClassProto + superType = typeProto; + } + + resolvedMethod = superType.getMethodByVtableIndex(methodIndex); + } else{ + resolvedMethod = objectRegisterTypeProto.getMethodByVtableIndex(methodIndex); + } + + if (resolvedMethod == null) { + throw new AnalysisException("Could not resolve the method in class %s at index %d", + objectRegisterType.type.getType(), methodIndex); + } + + // no need to check class access for invoke-super. A class can obviously access its superclass. + ClassDef thisClass = classPath.getClassDef(method.getDefiningClass()); + + if (!isSuper && !canAccessClass(thisClass, classPath.getClassDef(resolvedMethod.getDefiningClass()))) { + + // the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different + // than resolvedMethod.getDefiningClass()), and walk up the class hierarchy. + ClassDef methodClass = classPath.getClassDef(objectRegisterTypeProto.getType()); + while (!canAccessClass(thisClass, methodClass)) { + String superclass = methodClass.getSuperclass(); + if (superclass == null) { + throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s", + ReferenceUtil.getShortMethodDescriptor(resolvedMethod)); + } + + methodClass = classPath.getClassDef(superclass); + } + + // methodClass is now the first accessible class found. Now. we need to make sure that the method is + // actually valid for this class + resolvedMethod = classPath.getClass(methodClass.getType()).getMethodByVtableIndex(methodIndex); + if (resolvedMethod == null) { + throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s", + ReferenceUtil.getShortMethodDescriptor(resolvedMethod)); + } + resolvedMethod = new ImmutableMethodReference(methodClass.getType(), resolvedMethod.getName(), + resolvedMethod.getParameterTypes(), resolvedMethod.getReturnType()); + } + + Instruction deodexedInstruction; + if (isRange) { + Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction; + Opcode opcode; + if (isSuper) { + opcode = Opcode.INVOKE_SUPER_RANGE; + } else { + opcode = Opcode.INVOKE_VIRTUAL_RANGE; + } + + deodexedInstruction = new ImmutableInstruction3rc(opcode, instruction.getStartRegister(), + instruction.getRegisterCount(), resolvedMethod); + } else { + Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction; + Opcode opcode; + if (isSuper) { + opcode = Opcode.INVOKE_SUPER; + } else { + opcode = Opcode.INVOKE_VIRTUAL; + } + + deodexedInstruction = new ImmutableInstruction35c(opcode, instruction.getRegisterCount(), + instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(), + instruction.getRegisterF(), instruction.getRegisterG(), resolvedMethod); + } + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + analyzeInstruction(analyzedInstruction); + + return true; + } + + private boolean canAccessClass(@Nonnull ClassDef accessorClassDef, @Nonnull ClassDef accesseeClassDef) { + if (AccessFlags.PUBLIC.isSet(accesseeClassDef.getAccessFlags())) { + return true; + } + + // Classes can only be public or package private. Any private or protected inner classes are actually + // package private. + return getPackage(accesseeClassDef.getType()).equals(getPackage(accessorClassDef.getType())); + } + + private static String getPackage(String className) { + int lastSlash = className.lastIndexOf('/'); + if (lastSlash < 0) { + return ""; + } + return className.substring(1, lastSlash); + } + + private boolean analyzePutGetVolatile(@Nonnull AnalyzedInstruction analyzedInstruction) { + return analyzePutGetVolatile(analyzedInstruction, true); + } + + private boolean analyzePutGetVolatile(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) { + FieldReference field = (FieldReference)((ReferenceInstruction)analyzedInstruction.instruction).getReference(); + String fieldType = field.getType(); + + Opcode originalOpcode = analyzedInstruction.instruction.getOpcode(); + + Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, + originalOpcode); + + Instruction deodexedInstruction; + + if (originalOpcode.isOdexedStaticVolatile()) { + OneRegisterInstruction instruction = (OneRegisterInstruction)analyzedInstruction.instruction; + deodexedInstruction = new ImmutableInstruction21c(opcode, instruction.getRegisterA(), field); + } else { + TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction; + + deodexedInstruction = new ImmutableInstruction22c(opcode, instruction.getRegisterA(), + instruction.getRegisterB(), field); + } + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + if (analyzeResult) { + analyzeInstruction(analyzedInstruction); + } + return true; + } + + @Nonnull + private static RegisterType getAndCheckSourceRegister(@Nonnull AnalyzedInstruction analyzedInstruction, + int registerNumber, BitSet validCategories) { + assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length; + + RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber); + + checkRegister(registerType, registerNumber, validCategories); + + if (validCategories == WideLowCategories) { + checkRegister(registerType, registerNumber, WideLowCategories); + checkWidePair(registerNumber, analyzedInstruction); + + RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1); + checkRegister(secondRegisterType, registerNumber+1, WideHighCategories); + } + + return registerType; + } + + private static void checkRegister(RegisterType registerType, int registerNumber, BitSet validCategories) { + if (!validCategories.get(registerType.category)) { + throw new AnalysisException(String.format("Invalid register type %s for register v%d.", + registerType.toString(), registerNumber)); + } + } + + private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) { + if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) { + throw new AnalysisException(String.format("v%d cannot be used as the first register in a wide register" + + "pair because it is the last register.", registerNumber)); + } + } +} \ No newline at end of file diff --git a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java similarity index 55% rename from brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java rename to brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java index 644cbd3e..49136c35 100644 --- a/brut.apktool.smali/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/OdexedFieldInstructionMapper.java +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java @@ -1,34 +1,39 @@ /* - * [The "BSD licence"] - * Copyright (c) 2010 Ben Gruver + * 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: - * 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. + * modification, are permitted provided that the following conditions are + * met: * - * 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, + * * 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. + * (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; +package org.jf.dexlib2.analysis; -import org.jf.dexlib.Code.Opcode; +import org.jf.dexlib2.Opcode; + +import javax.annotation.Nonnull; public class OdexedFieldInstructionMapper { private static Opcode[][][][] opcodeMap = new Opcode[][][][] { @@ -178,107 +183,6 @@ public class OdexedFieldInstructionMapper { } }; - private static Opcode[][][][] jumboOpcodeMap = new Opcode[][][][] { - //get opcodes - new Opcode[][][] { - //iget volatile - new Opcode[][] { - //odexed - new Opcode[] { - /*Z*/ Opcode.IGET_VOLATILE_JUMBO, - /*B*/ Opcode.IGET_VOLATILE_JUMBO, - /*S*/ Opcode.IGET_VOLATILE_JUMBO, - /*C*/ Opcode.IGET_VOLATILE_JUMBO, - /*I,F*/ Opcode.IGET_VOLATILE_JUMBO, - /*J,D*/ Opcode.IGET_WIDE_VOLATILE_JUMBO, - /*L,[*/ Opcode.IGET_OBJECT_VOLATILE_JUMBO - }, - //deodexed - new Opcode[] { - /*Z*/ Opcode.IGET_BOOLEAN_JUMBO, - /*B*/ Opcode.IGET_BYTE_JUMBO, - /*S*/ Opcode.IGET_SHORT_JUMBO, - /*C*/ Opcode.IGET_CHAR_JUMBO, - /*I,F*/ Opcode.IGET_JUMBO, - /*J,D*/ Opcode.IGET_WIDE_JUMBO, - /*L,[*/ Opcode.IGET_OBJECT_JUMBO - } - }, - //sget volatile - new Opcode[][] { - //odexed - new Opcode[] { - /*Z*/ Opcode.SGET_VOLATILE_JUMBO, - /*B*/ Opcode.SGET_VOLATILE_JUMBO, - /*S*/ Opcode.SGET_VOLATILE_JUMBO, - /*C*/ Opcode.SGET_VOLATILE_JUMBO, - /*I,F*/ Opcode.SGET_VOLATILE_JUMBO, - /*J,D*/ Opcode.SGET_WIDE_VOLATILE_JUMBO, - /*L,[*/ Opcode.SGET_OBJECT_VOLATILE_JUMBO - }, - //deodexed - new Opcode[] { - /*Z*/ Opcode.SGET_BOOLEAN_JUMBO, - /*B*/ Opcode.SGET_BYTE_JUMBO, - /*S*/ Opcode.SGET_SHORT_JUMBO, - /*C*/ Opcode.SGET_CHAR_JUMBO, - /*I,F*/ Opcode.SGET_JUMBO, - /*J,D*/ Opcode.SGET_WIDE_JUMBO, - /*L,[*/ Opcode.SGET_OBJECT_JUMBO - } - } - }, - //put opcodes - new Opcode[][][] { - //iput volatile - new Opcode[][] { - //odexed - new Opcode[] { - /*Z*/ Opcode.IPUT_VOLATILE_JUMBO, - /*B*/ Opcode.IPUT_VOLATILE_JUMBO, - /*S*/ Opcode.IPUT_VOLATILE_JUMBO, - /*C*/ Opcode.IPUT_VOLATILE_JUMBO, - /*I,F*/ Opcode.IPUT_VOLATILE_JUMBO, - /*J,D*/ Opcode.IPUT_WIDE_VOLATILE_JUMBO, - /*L,[*/ Opcode.IPUT_OBJECT_VOLATILE_JUMBO - }, - //deodexed - new Opcode[] { - /*Z*/ Opcode.IPUT_BOOLEAN_JUMBO, - /*B*/ Opcode.IPUT_BYTE_JUMBO, - /*S*/ Opcode.IPUT_SHORT_JUMBO, - /*C*/ Opcode.IPUT_CHAR_JUMBO, - /*I,F*/ Opcode.IPUT_JUMBO, - /*J,D*/ Opcode.IPUT_WIDE_JUMBO, - /*L,[*/ Opcode.IPUT_OBJECT_JUMBO - } - }, - //sput volatile - new Opcode[][] { - //odexed - new Opcode[] { - /*Z*/ Opcode.SPUT_VOLATILE_JUMBO, - /*B*/ Opcode.SPUT_VOLATILE_JUMBO, - /*S*/ Opcode.SPUT_VOLATILE_JUMBO, - /*C*/ Opcode.SPUT_VOLATILE_JUMBO, - /*I,F*/ Opcode.SPUT_VOLATILE_JUMBO, - /*J,D*/ Opcode.SPUT_WIDE_VOLATILE_JUMBO, - /*L,[*/ Opcode.SPUT_OBJECT_VOLATILE_JUMBO - }, - //deodexed - new Opcode[] { - /*Z*/ Opcode.SPUT_BOOLEAN_JUMBO, - /*B*/ Opcode.SPUT_BYTE_JUMBO, - /*S*/ Opcode.SPUT_SHORT_JUMBO, - /*C*/ Opcode.SPUT_CHAR_JUMBO, - /*I,F*/ Opcode.SPUT_JUMBO, - /*J,D*/ Opcode.SPUT_WIDE_JUMBO, - /*L,[*/ Opcode.SPUT_OBJECT_JUMBO - } - } - } - }; - private static int getTypeIndex(char type) { switch (type) { case 'Z': @@ -303,7 +207,7 @@ public class OdexedFieldInstructionMapper { throw new RuntimeException(String.format("Unknown type %s: ", type)); } - private static int getOpcodeSubtype(Opcode opcode) { + private static int getOpcodeSubtype(@Nonnull Opcode opcode) { if (opcode.isOdexedInstanceQuick()) { return 0; } else if (opcode.isOdexedInstanceVolatile()) { @@ -314,24 +218,19 @@ public class OdexedFieldInstructionMapper { throw new RuntimeException(String.format("Not an odexed field access opcode: %s", opcode.name)); } - static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(String fieldType, Opcode odexedOpcode) { - boolean jumbo = odexedOpcode.isJumboOpcode(); + @Nonnull + static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) { int opcodeType = odexedOpcode.setsRegister()?0:1; int opcodeSubType = getOpcodeSubtype(odexedOpcode); int typeIndex = getTypeIndex(fieldType.charAt(0)); Opcode correctOdexedOpcode, deodexedOpcode; - if (jumbo) { - correctOdexedOpcode = jumboOpcodeMap[opcodeType][opcodeSubType-1][0][typeIndex]; - deodexedOpcode = jumboOpcodeMap[opcodeType][opcodeSubType-1][1][typeIndex]; - } else { - correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex]; - deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex]; - } + correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex]; + deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex]; if (correctOdexedOpcode != odexedOpcode) { - throw new ValidationException(String.format("Incorrect field type \"%s\" for %s", fieldType, + throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType, odexedOpcode.name)); } diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java new file mode 100644 index 00000000..06ab8e17 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java @@ -0,0 +1,71 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class PrimitiveProto implements TypeProto { + protected final ClassPath classPath; + protected final String type; + + public PrimitiveProto(@Nonnull ClassPath classPath, @Nonnull String type) { + this.classPath = classPath; + this.type = type; + } + + @Override public String toString() { return type; } + @Nonnull @Override public ClassPath getClassPath() { return classPath; } + @Nonnull @Override public String getType() { return type; } + @Override public boolean isInterface() { return false; } + @Override public boolean implementsInterface(@Nonnull String iface) { return false; } + @Nullable @Override public String getSuperclass() { return null; } + @Nonnull @Override public TypeProto getCommonSuperclass(@Nonnull TypeProto other) { + throw new ExceptionWithContext("Cannot call getCommonSuperclass on PrimitiveProto"); + } + + @Override + @Nullable + public FieldReference getFieldByOffset(int fieldOffset) { + return null; + } + + @Override + @Nullable + public MethodReference getMethodByVtableIndex(int vtableIndex) { + return null; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/RegisterType.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/RegisterType.java new file mode 100644 index 00000000..ba782fe6 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/RegisterType.java @@ -0,0 +1,351 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + + +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.Writer; + +public class RegisterType { + public final byte category; + @Nullable public final TypeProto type; + + private RegisterType(byte category, @Nullable TypeProto type) { + assert ((category == REFERENCE || category == UNINIT_REF || category == UNINIT_THIS) && type != null) || + ((category != REFERENCE && category != UNINIT_REF && category != UNINIT_THIS) && type == null); + + this.category = category; + this.type = type; + } + + @Override + public String toString() { + return "(" + CATEGORY_NAMES[category] + (type==null?"":("," + type)) + ")"; + } + + public void writeTo(Writer writer) throws IOException { + writer.write('('); + writer.write(CATEGORY_NAMES[category]); + if (type != null) { + writer.write(','); + writer.write(type.getType()); + } + 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; + } + + // These require strict reference equality. Every instance represents a unique + // reference that can't be merged with a different one, even if they have the same type. + if (category == UNINIT_REF || category == UNINIT_THIS) { + return false; + } + return (type != null ? type.equals(that.type) : that.type == null); + } + + @Override + public int hashCode() { + int result = category; + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } + + // The Unknown category denotes a register type that hasn't been determined yet + public static final byte UNKNOWN = 0; + // The Uninit category is for registers that haven't been set yet. e.g. the non-parameter registers in a method + // start out as unint + public static final byte UNINIT = 1; + public static final byte NULL = 2; + public static final byte ONE = 3; + public static final byte BOOLEAN = 4; + public static final byte BYTE = 5; + public static final byte POS_BYTE = 6; + public static final byte SHORT = 7; + public static final byte POS_SHORT = 8; + public static final byte CHAR = 9; + public static final byte INTEGER = 10; + public static final byte FLOAT = 11; + public static final byte LONG_LO = 12; + public static final byte LONG_HI = 13; + public static final byte DOUBLE_LO = 14; + public static final byte DOUBLE_HI = 15; + // The UninitRef category is used after a new-instance operation, and before the corresponding is called + public static final byte UNINIT_REF = 16; + // The UninitThis category is used the "this" register inside an method, before the superclass' + // method is called + public static final byte UNINIT_THIS = 17; + public static final byte REFERENCE = 18; + // 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 incoming 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. + public static final byte CONFLICTED = 19; + + public static final String[] CATEGORY_NAMES = new String[] { + "Unknown", + "Uninit", + "Null", + "One", + "Boolean", + "Byte", + "PosByte", + "Short", + "PosShort", + "Char", + "Integer", + "Float", + "LongLo", + "LongHi", + "DoubleLo", + "DoubleHi", + "UninitRef", + "UninitThis", + "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 byte[][] mergeTable = + { + /* UNKNOWN UNINIT NULL ONE, BOOLEAN BYTE POS_BYTE SHORT POS_SHORT CHAR INTEGER, FLOAT, LONG_LO LONG_HI DOUBLE_LO DOUBLE_HI UNINIT_REF UNINIT_THIS REFERENCE CONFLICTED*/ + /*UNKNOWN*/ {UNKNOWN, UNINIT, NULL, ONE, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, LONG_LO, LONG_HI, DOUBLE_LO, DOUBLE_HI, UNINIT_REF, UNINIT_THIS,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, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, REFERENCE, CONFLICTED}, + /*ONE*/ {ONE, CONFLICTED, BOOLEAN, ONE, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, CHAR, INTEGER, FLOAT, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, + /*BOOLEAN*/ {BOOLEAN, CONFLICTED, BOOLEAN, BOOLEAN, BOOLEAN, BYTE, POS_BYTE, SHORT, POS_SHORT, 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}, + /*POS_BYTE*/ {POS_BYTE, CONFLICTED, POS_BYTE, POS_BYTE, POS_BYTE, BYTE, POS_BYTE, SHORT, POS_SHORT, 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}, + /*POS_SHORT*/ {POS_SHORT, CONFLICTED, POS_SHORT, POS_SHORT, POS_SHORT, SHORT, POS_SHORT, SHORT, POS_SHORT, 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}, + /*LONG_LO*/ {LONG_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_LO, CONFLICTED, LONG_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, + /*LONG_HI*/ {LONG_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_HI, CONFLICTED, LONG_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, + /*DOUBLE_LO*/ {DOUBLE_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_LO, CONFLICTED, DOUBLE_LO, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, + /*DOUBLE_HI*/ {DOUBLE_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, LONG_HI, CONFLICTED, DOUBLE_HI, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, + /*UNINIT_REF*/ {UNINIT_REF, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED}, + /*UNINIT_THIS*/{UNINIT_THIS, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, CONFLICTED, UNINIT_THIS,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} + }; + + + public static final RegisterType UNKNOWN_TYPE = new RegisterType(UNKNOWN, null); + public static final RegisterType UNINIT_TYPE = new RegisterType(UNINIT, null); + public static final RegisterType NULL_TYPE = new RegisterType(NULL, null); + public static final RegisterType ONE_TYPE = new RegisterType(ONE, null); + public static final RegisterType BOOLEAN_TYPE = new RegisterType(BOOLEAN, null); + public static final RegisterType BYTE_TYPE = new RegisterType(BYTE, null); + public static final RegisterType POS_BYTE_TYPE = new RegisterType(POS_BYTE, null); + public static final RegisterType SHORT_TYPE = new RegisterType(SHORT, null); + public static final RegisterType POS_SHORT_TYPE = new RegisterType(POS_SHORT, null); + public static final RegisterType CHAR_TYPE = new RegisterType(CHAR, null); + public static final RegisterType INTEGER_TYPE = new RegisterType(INTEGER, null); + public static final RegisterType FLOAT_TYPE = new RegisterType(FLOAT, null); + public static final RegisterType LONG_LO_TYPE = new RegisterType(LONG_LO, null); + public static final RegisterType LONG_HI_TYPE = new RegisterType(LONG_HI, null); + public static final RegisterType DOUBLE_LO_TYPE = new RegisterType(DOUBLE_LO, null); + public static final RegisterType DOUBLE_HI_TYPE = new RegisterType(DOUBLE_HI, null); + public static final RegisterType CONFLICTED_TYPE = new RegisterType(CONFLICTED, null); + + @Nonnull + public static RegisterType getWideRegisterType(@Nonnull CharSequence type, boolean firstRegister) { + switch (type.charAt(0)) { + case 'J': + if (firstRegister) { + return getRegisterType(LONG_LO, null); + } else { + return getRegisterType(LONG_HI, null); + } + case 'D': + if (firstRegister) { + return getRegisterType(DOUBLE_LO, null); + } else { + return getRegisterType(DOUBLE_HI, null); + } + default: + throw new ExceptionWithContext("Cannot use this method for narrow register type: %s", type); + } + } + + @Nonnull + public static RegisterType getRegisterType(@Nonnull ClassPath classPath, @Nonnull CharSequence type) { + switch (type.charAt(0)) { + case 'Z': + return BOOLEAN_TYPE; + case 'B': + return BYTE_TYPE; + case 'S': + return SHORT_TYPE; + case 'C': + return CHAR_TYPE; + case 'I': + return INTEGER_TYPE; + case 'F': + return FLOAT_TYPE; + case 'J': + return LONG_LO_TYPE; + case 'D': + return DOUBLE_LO_TYPE; + case 'L': + case '[': + return getRegisterType(REFERENCE, classPath.getClass(type)); + default: + throw new ExceptionWithContext("Invalid type: " + type); + } + } + + @Nonnull + public static RegisterType getRegisterTypeForLiteral(int literalValue) { + if (literalValue < -32768) { + return INTEGER_TYPE; + } + if (literalValue < -128) { + return SHORT_TYPE; + } + if (literalValue < 0) { + return BYTE_TYPE; + } + if (literalValue == 0) { + return NULL_TYPE; + } + if (literalValue == 1) { + return ONE_TYPE; + } + if (literalValue < 128) { + return POS_BYTE_TYPE; + } + if (literalValue < 32768) { + return POS_SHORT_TYPE; + } + if (literalValue < 65536) { + return CHAR_TYPE; + } + return INTEGER_TYPE; + } + + @Nonnull + public RegisterType merge(@Nonnull RegisterType other) { + if (other.equals(this)) { + return this; + } + + byte mergedCategory = mergeTable[this.category][other.category]; + + TypeProto mergedType = null; + if (mergedCategory == REFERENCE) { + TypeProto type = this.type; + if (type != null) { + if (other.type != null) { + mergedType = type.getCommonSuperclass(other.type); + } else { + mergedType = type; + } + } else { + mergedType = other.type; + } + } else if (mergedCategory == UNINIT_REF || mergedCategory == UNINIT_THIS) { + if (this.category == UNKNOWN) { + return other; + } + assert other.category == UNKNOWN; + return this; + } + + if (mergedType != null) { + if (mergedType.equals(this.type)) { + return this; + } + if (mergedType.equals(other.type)) { + return other; + } + } + return RegisterType.getRegisterType(mergedCategory, mergedType); + } + + @Nonnull + public static RegisterType getRegisterType(byte category, @Nullable TypeProto typeProto) { + switch (category) { + case UNKNOWN: + return UNKNOWN_TYPE; + case UNINIT: + return UNINIT_TYPE; + case NULL: + return NULL_TYPE; + case ONE: + return ONE_TYPE; + case BOOLEAN: + return BOOLEAN_TYPE; + case BYTE: + return BYTE_TYPE; + case POS_BYTE: + return POS_BYTE_TYPE; + case SHORT: + return SHORT_TYPE; + case POS_SHORT: + return POS_SHORT_TYPE; + case CHAR: + return CHAR_TYPE; + case INTEGER: + return INTEGER_TYPE; + case FLOAT: + return FLOAT_TYPE; + case LONG_LO: + return LONG_LO_TYPE; + case LONG_HI: + return LONG_HI_TYPE; + case DOUBLE_LO: + return DOUBLE_LO_TYPE; + case DOUBLE_HI: + return DOUBLE_HI_TYPE; + case CONFLICTED: + return CONFLICTED_TYPE; + } + + return new RegisterType(category, typeProto); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java new file mode 100644 index 00000000..f6db2399 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface TypeProto { + @Nonnull ClassPath getClassPath(); + @Nonnull String getType(); + boolean isInterface(); + boolean implementsInterface(@Nonnull String iface); + @Nullable String getSuperclass(); + @Nonnull TypeProto getCommonSuperclass(@Nonnull TypeProto other); + @Nullable FieldReference getFieldByOffset(int fieldOffset); + @Nullable MethodReference getMethodByVtableIndex(int vtableIndex); +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java new file mode 100644 index 00000000..38256fbe --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java @@ -0,0 +1,81 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.MethodReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class UnknownClassProto implements TypeProto { + @Nonnull protected final ClassPath classPath; + + public UnknownClassProto(@Nonnull ClassPath classPath) { + this.classPath = classPath; + } + + @Override public String toString() { return "Ujava/lang/Object;"; } + @Nonnull @Override public ClassPath getClassPath() { return classPath; } + @Nullable @Override public String getSuperclass() { return null; } + @Override public boolean isInterface() { return false; } + @Override public boolean implementsInterface(@Nonnull String iface) { return false; } + + @Nonnull @Override public TypeProto getCommonSuperclass(@Nonnull TypeProto other) { + if (other.getType().equals("Ljava/lang/Object;")) { + return other; + } + if (other instanceof ArrayProto) { + // if it's an array class, it's safe to assume this unknown class isn't related, and so + // java.lang.Object is the only possible superclass + return classPath.getClass("Ljava/lang/Object;"); + } + return this; + } + + @Nonnull @Override public String getType() { + // use the otherwise used U prefix for an unknown/unresolvable class + return "Ujava/lang/Object;"; + } + + @Override + @Nullable + public FieldReference getFieldByOffset(int fieldOffset) { + return classPath.getClass("Ljava/lang/Object;").getFieldByOffset(fieldOffset); + } + + @Override + @Nullable + public MethodReference getMethodByVtableIndex(int vtableIndex) { + return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnresolvedClassException.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnresolvedClassException.java new file mode 100644 index 00000000..6ee36ca3 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnresolvedClassException.java @@ -0,0 +1,48 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import org.jf.util.ExceptionWithContext; + +public class UnresolvedClassException extends ExceptionWithContext { + public UnresolvedClassException(Throwable cause) { + super(cause); + } + + public UnresolvedClassException(Throwable cause, String message, Object... formatArgs) { + super(cause, message, formatArgs); + } + + public UnresolvedClassException(String message, Object... formatArgs) { + super(message, formatArgs); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnresolvedOdexInstruction.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnresolvedOdexInstruction.java new file mode 100644 index 00000000..fb201b01 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnresolvedOdexInstruction.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.instruction.Instruction; + +/** + * This represents a "fixed" odexed instruction, where the object register is always null and so the correct type + * can't be determined. Typically, these are replaced by an equivalent instruction that would have the same + * effect (namely, an NPE) + */ +public class UnresolvedOdexInstruction implements Instruction { + public final Instruction originalInstruction; + //the register number that holds the (null) reference type that the instruction operates on + public final int objectRegisterNum; + + public UnresolvedOdexInstruction(Instruction originalInstruction, int objectRegisterNumber) { + this.originalInstruction = originalInstruction; + this.objectRegisterNum = objectRegisterNumber; + } + + @Override public Opcode getOpcode() { + return originalInstruction.getOpcode(); + } + + @Override public int getCodeUnits() { + return originalInstruction.getCodeUnits(); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionClassDef.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionClassDef.java new file mode 100644 index 00000000..fc1dc62f --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionClassDef.java @@ -0,0 +1,251 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis.reflection; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterators; +import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils; +import org.jf.dexlib2.base.reference.BaseTypeReference; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.Field; +import org.jf.dexlib2.iface.Method; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Set; + +/** + * Wraps a ClassDef around a class loaded in the current VM + * + * Only supports the basic information exposed by ClassProto + */ +public class ReflectionClassDef extends BaseTypeReference implements ClassDef { + private final Class cls; + + public ReflectionClassDef(Class cls) { + this.cls = cls; + } + + @Override public int getAccessFlags() { + // the java modifiers appear to be the same as the dex access flags + return cls.getModifiers(); + } + + @Nullable @Override public String getSuperclass() { + if (Modifier.isInterface(cls.getModifiers())) { + return "Ljava/lang/Object;"; + } + Class superClass = cls.getSuperclass(); + if (superClass == null) { + return null; + } + return ReflectionUtils.javaToDexName(superClass.getName()); + } + + @Nonnull @Override public Set getInterfaces() { + return new AbstractSet() { + @Nonnull @Override public Iterator iterator() { + return Iterators.transform(Iterators.forArray(cls.getInterfaces()), new Function() { + @Nullable @Override public String apply(@Nullable Class input) { + if (input == null) { + return null; + } + return ReflectionUtils.javaToDexName(input.getName()); + } + }); + } + + @Override public int size() { + return cls.getInterfaces().length; + } + }; + } + + @Nullable @Override public String getSourceFile() { + return null; + } + + @Nonnull @Override public Set getAnnotations() { + return ImmutableSet.of(); + } + + @Nonnull @Override public Iterable getStaticFields() { + return new Iterable() { + @Nonnull @Override public Iterator iterator() { + Iterator staticFields = Iterators.filter( + Iterators.forArray(cls.getDeclaredFields()), + new Predicate() { + @Override public boolean apply(@Nullable java.lang.reflect.Field input) { + return input!=null && Modifier.isStatic(input.getModifiers()); + } + }); + + return Iterators.transform(staticFields, + new Function() { + @Nullable @Override public Field apply(@Nullable java.lang.reflect.Field input) { + return new ReflectionField(input); + } + } + ); + } + }; + } + + @Nonnull @Override public Iterable getInstanceFields() { + return new Iterable() { + @Nonnull @Override public Iterator iterator() { + Iterator staticFields = Iterators.filter( + Iterators.forArray(cls.getDeclaredFields()), + new Predicate() { + @Override public boolean apply(@Nullable java.lang.reflect.Field input) { + return input!=null && !Modifier.isStatic(input.getModifiers()); + } + }); + + return Iterators.transform(staticFields, + new Function() { + @Nullable @Override public Field apply(@Nullable java.lang.reflect.Field input) { + return new ReflectionField(input); + } + } + ); + } + }; + } + + @Nonnull @Override public Set getFields() { + return new AbstractSet() { + @Nonnull @Override public Iterator iterator() { + return Iterators.transform(Iterators.forArray(cls.getDeclaredFields()), + new Function() { + @Nullable @Override public Field apply(@Nullable java.lang.reflect.Field input) { + return new ReflectionField(input); + } + }); + } + + @Override public int size() { + return cls.getDeclaredFields().length; + } + }; + } + + private static final int DIRECT_MODIFIERS = Modifier.PRIVATE | Modifier.STATIC; + @Nonnull @Override public Iterable getDirectMethods() { + return new Iterable() { + @Nonnull @Override public Iterator iterator() { + Iterator constructorIterator = + Iterators.transform(Iterators.forArray(cls.getDeclaredConstructors()), + new Function() { + @Nullable @Override public Method apply(@Nullable Constructor input) { + return new ReflectionConstructor(input); + } + }); + + Iterator directMethods = Iterators.filter( + Iterators.forArray(cls.getDeclaredMethods()), + new Predicate() { + @Override public boolean apply(@Nullable java.lang.reflect.Method input) { + return input != null && (input.getModifiers() & DIRECT_MODIFIERS) != 0; + } + }); + + Iterator methodIterator = Iterators.transform(directMethods, + new Function() { + @Nullable @Override public Method apply(@Nullable java.lang.reflect.Method input) { + return new ReflectionMethod(input); + } + }); + return Iterators.concat(constructorIterator, methodIterator); + } + }; + } + + @Nonnull @Override public Iterable getVirtualMethods() { + return new Iterable() { + @Nonnull @Override public Iterator iterator() { + Iterator directMethods = Iterators.filter( + Iterators.forArray(cls.getDeclaredMethods()), + new Predicate() { + @Override public boolean apply(@Nullable java.lang.reflect.Method input) { + return input != null && (input.getModifiers() & DIRECT_MODIFIERS) == 0; + } + }); + + return Iterators.transform(directMethods, + new Function() { + @Nullable @Override public Method apply(@Nullable java.lang.reflect.Method input) { + return new ReflectionMethod(input); + } + }); + } + }; + } + + @Nonnull @Override public Set getMethods() { + return new AbstractSet() { + @Nonnull @Override public Iterator iterator() { + Iterator constructorIterator = + Iterators.transform(Iterators.forArray(cls.getDeclaredConstructors()), + new Function() { + @Nullable @Override public Method apply(@Nullable Constructor input) { + return new ReflectionConstructor(input); + } + }); + + Iterator methodIterator = + Iterators.transform(Iterators.forArray(cls.getDeclaredMethods()), + new Function() { + @Nullable @Override public Method apply(@Nullable java.lang.reflect.Method input) { + return new ReflectionMethod(input); + } + }); + return Iterators.concat(constructorIterator, methodIterator); + } + + @Override public int size() { + return cls.getDeclaredMethods().length + cls.getDeclaredConstructors().length; + } + }; + } + + @Nonnull @Override public String getType() { + return ReflectionUtils.javaToDexName(cls.getName()); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionConstructor.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionConstructor.java new file mode 100644 index 00000000..84219599 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionConstructor.java @@ -0,0 +1,121 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis.reflection; + +import com.google.common.collect.ImmutableSet; +import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils; +import org.jf.dexlib2.base.BaseMethodParameter; +import org.jf.dexlib2.base.reference.BaseMethodReference; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.iface.MethodParameter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.lang.reflect.Constructor; +import java.util.AbstractList; +import java.util.List; +import java.util.Set; + +public class ReflectionConstructor extends BaseMethodReference implements Method { + private final Constructor constructor; + + public ReflectionConstructor(Constructor constructor) { + this.constructor = constructor; + } + + @Nonnull @Override public List getParameters() { + final Constructor method = this.constructor; + return new AbstractList() { + private final Class[] parameters = method.getParameterTypes(); + + @Override public MethodParameter get(final int index) { + return new BaseMethodParameter() { + @Nonnull @Override public Set getAnnotations() { + return ImmutableSet.of(); + } + + @Nullable @Override public String getName() { + return null; + } + + @Nonnull @Override public String getType() { + return ReflectionUtils.javaToDexName(parameters[index].getName()); + } + }; + } + + @Override public int size() { + return parameters.length; + } + }; + } + + @Override public int getAccessFlags() { + return constructor.getModifiers(); + } + + @Nonnull @Override public Set getAnnotations() { + return ImmutableSet.of(); + } + + @Nullable @Override public MethodImplementation getImplementation() { + return null; + } + + @Nonnull @Override public String getDefiningClass() { + return ReflectionUtils.javaToDexName(constructor.getDeclaringClass().getName()); + } + + @Nonnull @Override public String getName() { + return constructor.getName(); + } + + @Nonnull @Override public List getParameterTypes() { + return new AbstractList() { + private final List parameters = getParameters(); + + @Override public String get(int index) { + return parameters.get(index).getType(); + } + + @Override public int size() { + return parameters.size(); + } + }; + } + + @Nonnull @Override public String getReturnType() { + return "V"; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionField.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionField.java new file mode 100644 index 00000000..c3a0e8c9 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionField.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis.reflection; + +import com.google.common.collect.ImmutableSet; +import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils; +import org.jf.dexlib2.base.reference.BaseFieldReference; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.Field; +import org.jf.dexlib2.iface.value.EncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Set; + +public class ReflectionField extends BaseFieldReference implements Field { + private final java.lang.reflect.Field field; + + public ReflectionField(java.lang.reflect.Field field) { + this.field = field; + } + + @Override public int getAccessFlags() { + return field.getModifiers(); + } + + @Nullable @Override public EncodedValue getInitialValue() { + return null; + } + + @Nonnull @Override public Set getAnnotations() { + return ImmutableSet.of(); + } + + @Nonnull @Override public String getDefiningClass() { + return ReflectionUtils.javaToDexName(field.getDeclaringClass().getName()); + } + + @Nonnull @Override public String getName() { + return field.getName(); + } + + @Nonnull @Override public String getType() { + return ReflectionUtils.javaToDexName(field.getType().getName()); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionMethod.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionMethod.java new file mode 100644 index 00000000..b7fb4756 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionMethod.java @@ -0,0 +1,120 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis.reflection; + +import com.google.common.collect.ImmutableSet; +import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils; +import org.jf.dexlib2.base.BaseMethodParameter; +import org.jf.dexlib2.base.reference.BaseMethodReference; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.iface.MethodParameter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.AbstractList; +import java.util.List; +import java.util.Set; + +public class ReflectionMethod extends BaseMethodReference implements Method { + private final java.lang.reflect.Method method; + + public ReflectionMethod(java.lang.reflect.Method method) { + this.method = method; + } + + @Nonnull @Override public List getParameters() { + final java.lang.reflect.Method method = this.method; + return new AbstractList() { + private final Class[] parameters = method.getParameterTypes(); + + @Override public MethodParameter get(final int index) { + return new BaseMethodParameter() { + @Nonnull @Override public Set getAnnotations() { + return ImmutableSet.of(); + } + + @Nullable @Override public String getName() { + return null; + } + + @Nonnull @Override public String getType() { + return ReflectionUtils.javaToDexName(parameters[index].getName()); + } + }; + } + + @Override public int size() { + return parameters.length; + } + }; + } + + @Override public int getAccessFlags() { + return method.getModifiers(); + } + + @Nonnull @Override public Set getAnnotations() { + return ImmutableSet.of(); + } + + @Nullable @Override public MethodImplementation getImplementation() { + return null; + } + + @Nonnull @Override public String getDefiningClass() { + return ReflectionUtils.javaToDexName(method.getDeclaringClass().getName()); + } + + @Nonnull @Override public String getName() { + return method.getName(); + } + + @Nonnull @Override public List getParameterTypes() { + return new AbstractList() { + private final List parameters = getParameters(); + + @Override public String get(int index) { + return parameters.get(index).getType(); + } + + @Override public int size() { + return parameters.size(); + } + }; + } + + @Nonnull @Override public String getReturnType() { + return ReflectionUtils.javaToDexName(method.getReturnType().getName()); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/util/ReflectionUtils.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/util/ReflectionUtils.java new file mode 100644 index 00000000..4a4615a6 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/util/ReflectionUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis.reflection.util; + +public class ReflectionUtils { + public static String javaToDexName(String javaName) { + javaName = javaName.replace('.', '/'); + if (javaName.length() > 1 && javaName.charAt(javaName.length()-1) != ';') { + javaName = 'L' + javaName + ';'; + } + return javaName; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java new file mode 100644 index 00000000..0313c7c3 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java @@ -0,0 +1,97 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis.util; + +import org.jf.dexlib2.analysis.TypeProto; +import org.jf.dexlib2.analysis.UnresolvedClassException; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class TypeProtoUtils { + /** + * Get the chain of superclasses of the given class. The first element will be the immediate superclass followed by + * it's superclass, etc. up to java.lang.Object. + * + * Returns an empty iterable if called on java.lang.Object or a primitive. + * + * If any class in the superclass chain can't be resolved, the iterable will return Ujava/lang/Object; to represent + * the unknown class. + * + * @return An iterable containing the superclasses of this class. + */ + @Nonnull + public static Iterable getSuperclassChain(@Nonnull final TypeProto typeProto) { + return new Iterable() { + + @Override public Iterator iterator() { + return new Iterator() { + @Nullable private TypeProto type = getSuperclassAsTypeProto(typeProto); + + @Override public boolean hasNext() { + return type != null; + } + + @Override public TypeProto next() { + TypeProto type = this.type; + if (type == null) { + throw new NoSuchElementException(); + } + + this.type = getSuperclassAsTypeProto(type); + return type; + } + + @Override public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + @Nullable + public static TypeProto getSuperclassAsTypeProto(@Nonnull TypeProto type) { + try { + String next = type.getSuperclass(); + if (next != null) { + return type.getClassPath().getClass(next); + } else { + return null; + } + } catch (UnresolvedClassException ex) { + return type.getClassPath().getUnknownClass(); + } + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotation.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotation.java new file mode 100644 index 00000000..134f3746 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotation.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.iface.Annotation; +import org.jf.util.CollectionUtils; + +import java.util.Comparator; + +public abstract class BaseAnnotation implements Annotation { + @Override + public int hashCode() { + int hashCode = getVisibility(); + hashCode = hashCode*31 + getType().hashCode(); + return hashCode*31 + getElements().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Annotation) { + Annotation other = (Annotation)o; + return (getVisibility() == other.getVisibility()) && + getType().equals(other.getType()) && + getElements().equals(other.getElements()); + } + return false; + } + + @Override + public int compareTo(Annotation o) { + int res = Ints.compare(getVisibility(), o.getVisibility()); + if (res != 0) return res; + res = getType().compareTo(o.getType()); + if (res != 0) return res; + return CollectionUtils.compareAsSet(getElements(), o.getElements()); + } + + public static final Comparator BY_TYPE = new Comparator() { + @Override + public int compare(Annotation annotation1, Annotation annotation2) { + return annotation1.getType().compareTo(annotation2.getType()); + } + }; +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotationElement.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotationElement.java new file mode 100644 index 00000000..92566cbc --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseAnnotationElement.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base; + +import org.jf.dexlib2.iface.AnnotationElement; + +import javax.annotation.Nonnull; +import java.util.Comparator; + +public abstract class BaseAnnotationElement implements AnnotationElement { + @Override + public int hashCode() { + int hashCode = getName().hashCode(); + return hashCode*31 + getValue().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o != null && o instanceof AnnotationElement) { + AnnotationElement other = (AnnotationElement)o; + return getName().equals(other.getName()) && + getValue().equals(other.getValue()); + } + return false; + } + + @Override + public int compareTo(AnnotationElement o) { + int res = getName().compareTo(o.getName()); + if (res != 0) return res; + return getValue().compareTo(o.getValue()); + } + + public static final Comparator BY_NAME = new Comparator() { + @Override + public int compare(@Nonnull AnnotationElement element1, @Nonnull AnnotationElement element2) { + return element1.getName().compareTo(element2.getName()); + } + }; +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseExceptionHandler.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseExceptionHandler.java new file mode 100644 index 00000000..9ad6b648 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseExceptionHandler.java @@ -0,0 +1,113 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base; + +import com.google.common.base.Objects; +import com.google.common.primitives.Ints; +import org.jf.dexlib2.base.reference.BaseTypeReference; +import org.jf.dexlib2.iface.ExceptionHandler; +import org.jf.dexlib2.iface.reference.TypeReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Comparator; + +public abstract class BaseExceptionHandler implements ExceptionHandler { + @Nullable @Override public TypeReference getExceptionTypeReference() { + final String exceptionType = getExceptionType(); + if (exceptionType == null) { + return null; + } + + return new BaseTypeReference() { + @Nonnull @Override public String getType() { + return exceptionType; + } + }; + } + + @Override + public int hashCode() { + String exceptionType = getExceptionType(); + int hashCode = exceptionType==null?0:exceptionType.hashCode(); + return hashCode*31 + getHandlerCodeAddress(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof ExceptionHandler) { + ExceptionHandler other = (ExceptionHandler)o; + return Objects.equal(getExceptionType(), other.getExceptionType()) && + (getHandlerCodeAddress() == other.getHandlerCodeAddress()); + } + return false; + } + + @Override + public int compareTo(@Nonnull ExceptionHandler o) { + int res; + String exceptionType = getExceptionType(); + if (exceptionType == null) { + if (o.getExceptionType() != null) { + return 1; + } + } else { + String otherExceptionType = o.getExceptionType(); + if (otherExceptionType == null) { + return -1; + } + res = exceptionType.compareTo(o.getExceptionType()); + if (res != 0) return res; + } + return Ints.compare(getHandlerCodeAddress(), o.getHandlerCodeAddress()); + } + + + + public static final Comparator BY_EXCEPTION = new Comparator() { + @Override public int compare(ExceptionHandler o1, ExceptionHandler o2) { + String exceptionType1 = o1.getExceptionType(); + if (exceptionType1 == null) { + if (o2.getExceptionType() != null) { + return 1; + } + return 0; + } else { + String exceptionType2 = o2.getExceptionType(); + if (exceptionType2 == null) { + return -1; + } + return exceptionType1.compareTo(o2.getExceptionType()); + } + } + }; +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseMethodParameter.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseMethodParameter.java new file mode 100644 index 00000000..ecac3c96 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseMethodParameter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base; + +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.base.reference.BaseTypeReference; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.AnnotationElement; +import org.jf.dexlib2.iface.MethodParameter; +import org.jf.dexlib2.iface.value.ArrayEncodedValue; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.StringEncodedValue; + +import javax.annotation.Nullable; + +public abstract class BaseMethodParameter extends BaseTypeReference implements MethodParameter { + @Nullable + @Override + public String getSignature() { + Annotation signatureAnnotation = null; + for (Annotation annotation: getAnnotations()) { + if (annotation.getType().equals("Ldalvik/annotation/Signature;")) { + signatureAnnotation = annotation; + break; + } + } + if (signatureAnnotation == null) { + return null; + } + + ArrayEncodedValue signatureValues = null; + for (AnnotationElement annotationElement: signatureAnnotation.getElements()) { + if (annotationElement.getName().equals("value")) { + EncodedValue encodedValue = annotationElement.getValue(); + if (encodedValue.getValueType() != ValueType.ARRAY) { + return null; + } + signatureValues = (ArrayEncodedValue)encodedValue; + break; + } + } + if (signatureValues == null) { + return null; + } + + StringBuilder sb = new StringBuilder(); + for (EncodedValue signatureValue: signatureValues.getValue()) { + if (signatureValue.getValueType() != ValueType.STRING) { + return null; + } + sb.append(((StringEncodedValue)signatureValue).getValue()); + } + return sb.toString(); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseTryBlock.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseTryBlock.java new file mode 100644 index 00000000..d1afe9a4 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/BaseTryBlock.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base; + +import org.jf.dexlib2.iface.ExceptionHandler; +import org.jf.dexlib2.iface.TryBlock; + +public abstract class BaseTryBlock implements TryBlock { + @Override public boolean equals(Object o) { + if (o instanceof TryBlock) { + TryBlock other = (TryBlock)o; + return getStartCodeAddress() == other.getStartCodeAddress() && + getCodeUnitCount() == other.getCodeUnitCount() && + getExceptionHandlers().equals(other.getExceptionHandlers()); + } + return false; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseFieldReference.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseFieldReference.java new file mode 100644 index 00000000..f056f245 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseFieldReference.java @@ -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.dexlib2.base.reference; + +import org.jf.dexlib2.iface.reference.FieldReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseFieldReference implements FieldReference { + @Override + public int hashCode() { + int hashCode = getDefiningClass().hashCode(); + hashCode = hashCode*31 + getName().hashCode(); + return hashCode*31 + getType().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof FieldReference) { + FieldReference other = (FieldReference)o; + return getDefiningClass().equals(other.getDefiningClass()) && + getName().equals(other.getName()) && + getType().equals(other.getType()); + + } + return false; + } + + @Override + public int compareTo(@Nonnull FieldReference o) { + int res = getDefiningClass().compareTo(o.getDefiningClass()); + if (res != 0) return res; + res = getName().compareTo(o.getName()); + if (res != 0) return res; + return getType().compareTo(o.getType()); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodReference.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodReference.java new file mode 100644 index 00000000..3ff6f7db --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodReference.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.reference; + +import com.google.common.collect.Ordering; +import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.util.CharSequenceUtils; +import org.jf.util.CollectionUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseMethodReference implements MethodReference { + @Override + public int hashCode() { + int hashCode = getDefiningClass().hashCode(); + hashCode = hashCode*31 + getName().hashCode(); + hashCode = hashCode*31 + getReturnType().hashCode(); + return hashCode*31 + getParameterTypes().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o != null && o instanceof MethodReference) { + MethodReference other = (MethodReference)o; + return getDefiningClass().equals(other.getDefiningClass()) && + getName().equals(other.getName()) && + getReturnType().equals(other.getReturnType()) && + CharSequenceUtils.listEquals(getParameterTypes(), other.getParameterTypes()); + } + return false; + } + + @Override + public int compareTo(@Nonnull MethodReference o) { + int res = getDefiningClass().compareTo(o.getDefiningClass()); + if (res != 0) return res; + res = getName().compareTo(o.getName()); + if (res != 0) return res; + res = getReturnType().compareTo(o.getReturnType()); + if (res != 0) return res; + return CollectionUtils.compareAsIterable(Ordering.usingToString(), getParameterTypes(), o.getParameterTypes()); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseStringReference.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseStringReference.java new file mode 100644 index 00000000..c6daa91e --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseStringReference.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.reference; + +import org.jf.dexlib2.iface.reference.StringReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseStringReference implements StringReference { + @Override + public int hashCode() { + return getString().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o != null && o instanceof StringReference) { + return getString().equals(((StringReference)o).getString()); + } + return false; + } + + @Override + public int compareTo(@Nonnull CharSequence o) { + return getString().compareTo(o.toString()); + } + + @Override public int length() { return getString().length(); } + @Override public char charAt(int index) { return getString().charAt(index); } + @Override public CharSequence subSequence(int start, int end) { return getString().subSequence(start, end); } + @Override public String toString() { return getString(); } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseTypeReference.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseTypeReference.java new file mode 100644 index 00000000..4b24d071 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseTypeReference.java @@ -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.dexlib2.base.reference; + +import org.jf.dexlib2.iface.reference.TypeReference; + +import javax.annotation.Nonnull; + +public abstract class BaseTypeReference implements TypeReference { + @Override + public int hashCode() { + return getType().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o != null) { + if (o instanceof TypeReference) { + return getType().equals(((TypeReference)o).getType()); + } + if (o instanceof CharSequence) { + return getType().equals(o.toString()); + } + } + return false; + } + + @Override + public int compareTo(@Nonnull CharSequence o) { + return getType().compareTo(o.toString()); + } + + @Override public int length() { return getType().length(); } + @Override public char charAt(int index) { return getType().charAt(index); } + @Override public CharSequence subSequence(int start, int end) { return getType().subSequence(start, end); } + @Override @Nonnull public String toString() { return getType(); } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseAnnotationEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseAnnotationEncodedValue.java new file mode 100644 index 00000000..828c1eca --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseAnnotationEncodedValue.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.AnnotationEncodedValue; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.util.CollectionUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseAnnotationEncodedValue implements AnnotationEncodedValue { + @Override + public int hashCode() { + int hashCode = getType().hashCode(); + return hashCode * 31 + getElements().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof AnnotationEncodedValue) { + AnnotationEncodedValue other = (AnnotationEncodedValue)o; + return getType().equals(other.getType()) && + getElements().equals(other.getElements()); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + AnnotationEncodedValue other = (AnnotationEncodedValue)o; + res = getType().compareTo(other.getType()); + if (res != 0) return res; + return CollectionUtils.compareAsSet(getElements(), other.getElements()); + } + + public int getValueType() { + return ValueType.ANNOTATION; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseArrayEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseArrayEncodedValue.java new file mode 100644 index 00000000..7c6efd3e --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseArrayEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.ArrayEncodedValue; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.util.CollectionUtils; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseArrayEncodedValue implements ArrayEncodedValue { + @Override + public int hashCode() { + return getValue().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof ArrayEncodedValue) { + return getValue().equals(((ArrayEncodedValue)o).getValue()); + } + return false; + } + + @Override public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return CollectionUtils.compareAsList(getValue(), ((ArrayEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.ARRAY; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseBooleanEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseBooleanEncodedValue.java new file mode 100644 index 00000000..aeedc2c0 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseBooleanEncodedValue.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Booleans; +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.BooleanEncodedValue; +import org.jf.dexlib2.iface.value.EncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseBooleanEncodedValue implements BooleanEncodedValue { + @Override + public int hashCode() { + return getValue()?1:0; + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof BooleanEncodedValue) { + return getValue() == ((BooleanEncodedValue)o).getValue(); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return Booleans.compare(getValue(), ((BooleanEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.BOOLEAN; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseByteEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseByteEncodedValue.java new file mode 100644 index 00000000..5ec16241 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseByteEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.ByteEncodedValue; +import org.jf.dexlib2.iface.value.EncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseByteEncodedValue implements ByteEncodedValue { + @Override + public int hashCode() { + return getValue(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof ByteEncodedValue) { + return getValue() == ((ByteEncodedValue)o).getValue(); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return Ints.compare(getValue(), ((ByteEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.BYTE; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseCharEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseCharEncodedValue.java new file mode 100644 index 00000000..9f076e28 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseCharEncodedValue.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Chars; +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.CharEncodedValue; +import org.jf.dexlib2.iface.value.EncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseCharEncodedValue implements CharEncodedValue { + @Override + public int hashCode() { + return getValue(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof CharEncodedValue) { + return getValue() == ((CharEncodedValue)o).getValue(); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return Chars.compare(getValue(), ((CharEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.CHAR; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseDoubleEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseDoubleEncodedValue.java new file mode 100644 index 00000000..d8b08c36 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseDoubleEncodedValue.java @@ -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.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.DoubleEncodedValue; +import org.jf.dexlib2.iface.value.EncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseDoubleEncodedValue implements DoubleEncodedValue { + @Override + public int hashCode() { + long v = Double.doubleToRawLongBits(getValue()); + return (int)(v^(v>>>32)); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof DoubleEncodedValue) { + return Double.doubleToRawLongBits(getValue()) == + Double.doubleToRawLongBits(((DoubleEncodedValue)o).getValue()); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return Double.compare(getValue(), ((DoubleEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.DOUBLE; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseEnumEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseEnumEncodedValue.java new file mode 100644 index 00000000..ea2ec121 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseEnumEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.EnumEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseEnumEncodedValue implements EnumEncodedValue { + @Override + public int hashCode() { + return getValue().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof EnumEncodedValue) { + return getValue().equals(((EnumEncodedValue)o).getValue()); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return getValue().compareTo(((EnumEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.ENUM; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseFieldEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseFieldEncodedValue.java new file mode 100644 index 00000000..8d4f3d91 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseFieldEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.FieldEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseFieldEncodedValue implements FieldEncodedValue { + @Override + public int hashCode() { + return getValue().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof FieldEncodedValue) { + return getValue().equals(((FieldEncodedValue)o).getValue()); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return getValue().compareTo(((FieldEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.FIELD; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseFloatEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseFloatEncodedValue.java new file mode 100644 index 00000000..50925679 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseFloatEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.FloatEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseFloatEncodedValue implements FloatEncodedValue { + @Override + public int hashCode() { + return Float.floatToRawIntBits(getValue()); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o != null && o instanceof FloatEncodedValue) { + return Float.floatToRawIntBits(getValue()) == Float.floatToRawIntBits(((FloatEncodedValue)o).getValue()); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return Float.compare(getValue(), ((FloatEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.FLOAT; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseIntEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseIntEncodedValue.java new file mode 100644 index 00000000..94f7e146 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseIntEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.IntEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseIntEncodedValue implements IntEncodedValue { + @Override + public int hashCode() { + return getValue(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof IntEncodedValue) { + return getValue() == ((IntEncodedValue)o).getValue(); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return Ints.compare(getValue(), ((IntEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.INT; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseLongEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseLongEncodedValue.java new file mode 100644 index 00000000..8808dda6 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseLongEncodedValue.java @@ -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.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.LongEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseLongEncodedValue implements LongEncodedValue { + @Override + public int hashCode() { + long value = getValue(); + int hashCode = (int)value; + return hashCode*31 + (int)(value>>>32); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof LongEncodedValue) { + return getValue() == ((LongEncodedValue)o).getValue(); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return Longs.compare(getValue(), ((LongEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.LONG; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseMethodEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseMethodEncodedValue.java new file mode 100644 index 00000000..71492147 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseMethodEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.MethodEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseMethodEncodedValue implements MethodEncodedValue { + @Override + public int hashCode() { + return getValue().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof MethodEncodedValue) { + return getValue().equals(((MethodEncodedValue)o).getValue()); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return getValue().compareTo(((MethodEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.METHOD; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseNullEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseNullEncodedValue.java new file mode 100644 index 00000000..923619bd --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseNullEncodedValue.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.NullEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseNullEncodedValue implements NullEncodedValue { + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(@Nullable Object o) { + return o instanceof NullEncodedValue; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + return Ints.compare(getValueType(), o.getValueType()); + } + + public int getValueType() { return ValueType.NULL; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseShortEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseShortEncodedValue.java new file mode 100644 index 00000000..88aabaa8 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseShortEncodedValue.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import com.google.common.primitives.Shorts; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.ShortEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseShortEncodedValue implements ShortEncodedValue { + @Override + public int hashCode() { + return getValue(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof ShortEncodedValue) { + return getValue() == ((ShortEncodedValue)o).getValue(); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return Shorts.compare(getValue(), ((ShortEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.SHORT; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseStringEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseStringEncodedValue.java new file mode 100644 index 00000000..a9bd6cdd --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseStringEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.StringEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseStringEncodedValue implements StringEncodedValue { + @Override + public int hashCode() { + return getValue().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof StringEncodedValue) { + return getValue().equals(((StringEncodedValue)o).getValue()); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return getValue().compareTo(((StringEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.STRING; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseTypeEncodedValue.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseTypeEncodedValue.java new file mode 100644 index 00000000..80f7399d --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/base/value/BaseTypeEncodedValue.java @@ -0,0 +1,64 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.value; + +import com.google.common.primitives.Ints; +import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.value.EncodedValue; +import org.jf.dexlib2.iface.value.TypeEncodedValue; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BaseTypeEncodedValue implements TypeEncodedValue { + @Override + public int hashCode() { + return getValue().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof TypeEncodedValue) { + return getValue().equals(((TypeEncodedValue)o).getValue()); + } + return false; + } + + @Override + public int compareTo(@Nonnull EncodedValue o) { + int res = Ints.compare(getValueType(), o.getValueType()); + if (res != 0) return res; + return getValue().compareTo(((TypeEncodedValue)o).getValue()); + } + + public int getValueType() { return ValueType.TYPE; } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderDebugItem.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderDebugItem.java new file mode 100644 index 00000000..0969a9db --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderDebugItem.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import org.jf.dexlib2.iface.debug.DebugItem; + +import javax.annotation.Nullable; + +public abstract class BuilderDebugItem implements DebugItem { + @Nullable MethodLocation location; + + public BuilderDebugItem() { + } + + @Override public int getCodeAddress() { + if (location == null) { + throw new IllegalStateException("Cannot get the address of a BuilderDebugItem that isn't associated with " + + "a method."); + } + return location.getCodeAddress(); + } + +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderExceptionHandler.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderExceptionHandler.java new file mode 100644 index 00000000..e7376df0 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderExceptionHandler.java @@ -0,0 +1,99 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import org.jf.dexlib2.base.BaseExceptionHandler; +import org.jf.dexlib2.iface.reference.TypeReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BuilderExceptionHandler extends BaseExceptionHandler { + @Nonnull protected final Label handler; + + private BuilderExceptionHandler(@Nonnull Label handler) { + this.handler = handler; + } + + @Nonnull + public Label getHandler() { + return handler; + } + + static BuilderExceptionHandler newExceptionHandler(@Nullable final TypeReference exceptionType, + @Nonnull Label handler) { + if (exceptionType == null) { + return newExceptionHandler(handler); + } + return new BuilderExceptionHandler(handler) { + @Nullable @Override public String getExceptionType() { + return exceptionType.getType(); + } + + @Override public int getHandlerCodeAddress() { + return handler.getCodeAddress(); + } + + @Nullable @Override public TypeReference getExceptionTypeReference() { + return exceptionType; + } + }; + } + + static BuilderExceptionHandler newExceptionHandler(@Nonnull Label handler) { + return new BuilderExceptionHandler(handler) { + @Nullable @Override public String getExceptionType() { + return null; + } + + @Override public int getHandlerCodeAddress() { + return handler.getCodeAddress(); + } + }; + } + + static BuilderExceptionHandler newExceptionHandler(@Nullable final String exceptionType, + @Nonnull Label handler) { + if (exceptionType == null) { + return newExceptionHandler(handler); + } + return new BuilderExceptionHandler(handler) { + @Nullable @Override public String getExceptionType() { + return exceptionType; + } + + @Override public int getHandlerCodeAddress() { + return handler.getCodeAddress(); + } + }; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderInstruction.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderInstruction.java new file mode 100644 index 00000000..d06f7a8b --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderInstruction.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import org.jf.dexlib2.Format; +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.util.Preconditions; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class BuilderInstruction implements Instruction { + @Nonnull protected final Opcode opcode; + + @Nullable MethodLocation location; + + protected BuilderInstruction(@Nonnull Opcode opcode) { + Preconditions.checkFormat(opcode, getFormat()); + this.opcode = opcode; + } + + @Nonnull public Opcode getOpcode() { + return opcode; + } + + public abstract Format getFormat(); + + public int getCodeUnits() { + return getFormat().size / 2; + } + + @Nonnull + public MethodLocation getLocation() { + if (location == null) { + throw new IllegalStateException("Cannot get the location of an instruction that hasn't been added to a " + + "method."); + } + return location; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderOffsetInstruction.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderOffsetInstruction.java new file mode 100644 index 00000000..d75d7b67 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderOffsetInstruction.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.iface.instruction.OffsetInstruction; + +import javax.annotation.Nonnull; + +public abstract class BuilderOffsetInstruction extends BuilderInstruction implements OffsetInstruction { + @Nonnull + protected final Label target; + + public BuilderOffsetInstruction(@Nonnull Opcode opcode, + @Nonnull Label target) { + super(opcode); + this.target = target; + } + + @Override public int getCodeOffset() { + int codeOffset = internalGetCodeOffset(); + if ((this.getCodeUnits() == 1 && (codeOffset < Byte.MIN_VALUE || codeOffset > Byte.MAX_VALUE)) || + (this.getCodeUnits() == 2 && (codeOffset < Short.MIN_VALUE || codeOffset > Short.MAX_VALUE))) { + throw new IllegalStateException("Target is out of range"); + } + return codeOffset; + } + + + int internalGetCodeOffset() { + return target.getCodeAddress() - this.getLocation().getCodeAddress(); + } + + @Nonnull + public Label getTarget() { + return target; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderSwitchPayload.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderSwitchPayload.java new file mode 100644 index 00000000..2fd490bd --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderSwitchPayload.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.builder.instruction.BuilderSwitchElement; +import org.jf.dexlib2.iface.instruction.SwitchPayload; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public abstract class BuilderSwitchPayload extends BuilderInstruction implements SwitchPayload { + @Nullable + MethodLocation referrer; + + protected BuilderSwitchPayload(@Nonnull Opcode opcode) { + super(opcode); + } + + @Nonnull + public MethodLocation getReferrer() { + if (referrer == null) { + throw new IllegalStateException("The referrer has not been set yet"); + } + return referrer; + } + + @Nonnull @Override public abstract List getSwitchElements(); +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderTryBlock.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderTryBlock.java new file mode 100644 index 00000000..f2387888 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/BuilderTryBlock.java @@ -0,0 +1,81 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.base.BaseTryBlock; +import org.jf.dexlib2.iface.reference.TypeReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class BuilderTryBlock extends BaseTryBlock { + // We only ever have one exception handler per try block. They are later merged as needed in TryListBuilder + @Nonnull public final BuilderExceptionHandler exceptionHandler; + @Nonnull public final Label start; + // The end location is exclusive, it should point to the codeAddress of the instruction immediately after the last + // covered instruction. + @Nonnull public final Label end; + + public BuilderTryBlock(@Nonnull Label start, @Nonnull Label end, @Nullable String exceptionType, + @Nonnull Label handler) { + this.start = start; + this.end = end; + this.exceptionHandler = BuilderExceptionHandler.newExceptionHandler(exceptionType, handler); + } + + public BuilderTryBlock(@Nonnull Label start, @Nonnull Label end, @Nullable TypeReference exceptionType, + @Nonnull Label handler) { + this.start = start; + this.end = end; + this.exceptionHandler = BuilderExceptionHandler.newExceptionHandler(exceptionType, handler); + } + + public BuilderTryBlock(@Nonnull Label start, @Nonnull Label end, @Nonnull Label handler) { + this.start = start; + this.end = end; + this.exceptionHandler = BuilderExceptionHandler.newExceptionHandler(handler); + } + + @Override public int getStartCodeAddress() { + return start.getCodeAddress(); + } + + @Override public int getCodeUnitCount() { + return end.getCodeAddress() - start.getCodeAddress(); + } + + @Nonnull @Override public List getExceptionHandlers() { + return ImmutableList.of(exceptionHandler); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/Label.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/Label.java new file mode 100644 index 00000000..ef19f91c --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/Label.java @@ -0,0 +1,62 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class Label { + @Nullable MethodLocation location; + + Label() { + } + + Label(MethodLocation location) { + this.location = location; + } + + public int getCodeAddress() { + return getLocation().getCodeAddress(); + } + + @Nonnull + public MethodLocation getLocation() { + if (location == null) { + throw new IllegalStateException("Cannot get the location of a label that hasn't been placed yet."); + } + return location; + } + + public boolean isPlaced() { + return location != null; + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodImplementationBuilder.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodImplementationBuilder.java new file mode 100644 index 00000000..e830b655 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodImplementationBuilder.java @@ -0,0 +1,153 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.iface.reference.StringReference; +import org.jf.dexlib2.iface.reference.TypeReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.HashMap; + +public class MethodImplementationBuilder { + // Contains all named labels - both placed and unplaced + private final HashMap labels = new HashMap(); + + @Nonnull + private final MutableMethodImplementation impl; + + private MethodLocation currentLocation; + + public MethodImplementationBuilder(int registerCount) { + this.impl = new MutableMethodImplementation(registerCount); + this.currentLocation = impl.instructionList.get(0); + } + + public MethodImplementation getMethodImplementation() { + return impl; + } + + /** + * Adds a new named label at the current location. + * + * Any previous unplaced references to a label of this name will now refer to this label/location + * + * @param name The name of the label to add + * @return A LabelRef representing the label + */ + @Nonnull + public Label addLabel(@Nonnull String name) { + Label label = labels.get(name); + + if (label != null) { + if (label.isPlaced()) { + throw new IllegalArgumentException("There is already a label with that name."); + } else { + currentLocation.getLabels().add(label); + } + } else { + label = currentLocation.addNewLabel(); + labels.put(name, label); + } + + return label; + } + + /** + * Get a reference to a label with the given name. + * + * If a label with that name has not been added yet, a new one is created, but is left + * in an unplaced state. It is assumed that addLabel(name) will be called at a later + * point to define the location of the label. + * + * @param name The name of the label to get + * @return A LabelRef representing the label + */ + @Nonnull + public Label getLabel(@Nonnull String name) { + Label label = labels.get(name); + if (label == null) { + label = new Label(); + labels.put(name, label); + } + return label; + } + + public void addCatch(@Nullable TypeReference type, @Nonnull Label from, + @Nonnull Label to, @Nonnull Label handler) { + impl.addCatch(type, from, to, handler); + } + + public void addCatch(@Nullable String type, @Nonnull Label from, @Nonnull Label to, + @Nonnull Label handler) { + impl.addCatch(type, from, to, handler); + } + + public void addCatch(@Nonnull Label from, @Nonnull Label to, @Nonnull Label handler) { + impl.addCatch(from, to, handler); + } + + public void addLineNumber(int lineNumber) { + currentLocation.addLineNumber(lineNumber); + } + + public void addStartLocal(int registerNumber, @Nullable StringReference name, @Nullable TypeReference type, + @Nullable StringReference signature) { + currentLocation.addStartLocal(registerNumber, name, type, signature); + } + + public void addEndLocal(int registerNumber) { + currentLocation.addEndLocal(registerNumber); + } + + public void addRestartLocal(int registerNumber) { + currentLocation.addRestartLocal(registerNumber); + } + + public void addPrologue() { + currentLocation.addPrologue(); + } + + public void addEpilogue() { + currentLocation.addEpilogue(); + } + + public void addSetSourceFile(@Nullable StringReference sourceFile) { + currentLocation.addSetSourceFile(sourceFile); + } + + public void addInstruction(@Nullable BuilderInstruction instruction) { + impl.addInstruction(instruction); + currentLocation = impl.instructionList.get(impl.instructionList.size()-1); + } +} diff --git a/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java new file mode 100644 index 00000000..5a707e92 --- /dev/null +++ b/brut.apktool.smali/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java @@ -0,0 +1,247 @@ +/* + * Copyright 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.builder; + +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.builder.debug.*; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.reference.StringReference; +import org.jf.dexlib2.iface.reference.TypeReference; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +public class MethodLocation { + @Nullable BuilderInstruction instruction; + int codeAddress; + int index; + + // We end up creating and keeping around a *lot* of MethodLocation objects + // when building a new dex file, so it's worth the trouble of lazily creating + // the labels and debugItems lists only when they are needed + + @Nullable + private List
    ]\n" - + " Decode to .\n" - + "\n" - + " OPTS:\n" - + "\n" - + " -s, --no-src\n" - + " Do not decode sources.\n" - + " -r, --no-res\n" - + " Do not decode resources.\n" - + " -d, --debug\n" - + " Decode in debug mode. Check project page for more info.\n" - + " -b, --no-debug-info\n" - + " Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n" - + " -f, --force\n" - + " Force delete destination directory.\n" - + " -t , --frame-tag \n" - + " Try to use framework files tagged by .\n" - + " --frame-path \n" - + " Use the specified directory for framework files\n" - + " --keep-broken-res\n" - + " Use if there was an error and some resources were dropped, e.g.:\n" - + " \"Invalid config flags detected. Dropping resources\", but you\n" - + " want to decode them anyway, even with errors. You will have to\n" - + " fix them manually before building." - + "\n\n" - + " b[uild] [OPTS] [] []\n" - + " Build an apk from already decoded application located in .\n" - + "\n" - + " It will automatically detect, whether files was changed and perform\n" - + " needed steps only.\n" - + "\n" - + " If you omit then current directory will be used.\n" - + " If you omit then /dist/\n" - + " will be used.\n" - + "\n" - + " OPTS:\n" - + "\n" - + " -f, --force-all\n" - + " Skip changes detection and build all files.\n" - + " -d, --debug\n" - + " Build in debug mode. Check project page for more info.\n" - + " -a, --aapt\n" - + " Loads aapt from specified location.\n" - + " --frame-path \n" - + " Use the specified directory for framework files\n" - + "\n" - + " if|install-framework [] --frame-path [] \n" - + " Install framework file to your system.\n" - + "\n" - + "For additional info, see: http://code.google.com/p/android-apktool/" - + "\n" - + "For smali/baksmali info, see: http://code.google.com/p/smali/"); - } + // hold all the fields + HashMap flags = new HashMap(); + flags.put("forceBuildAll", false); + flags.put("debug", false); + flags.put("verbose", false); + flags.put("framework", false); + flags.put("update", false); + flags.put("copyOriginal", false); - private static void setupLogging(Verbosity verbosity) { - Logger logger = Logger.getLogger(""); - for (Handler handler : logger.getHandlers()) { - logger.removeHandler(handler); - } - if (verbosity == Verbosity.QUIET) { - return; - } + // check for build options + if (cli.hasOption("f") || cli.hasOption("force-all")) { + flags.put("forceBuildAll", true); + } + if (cli.hasOption("d") || cli.hasOption("debug")) { + flags.put("debug", true); + } + if (cli.hasOption("v") || cli.hasOption("verbose")) { + flags.put("verbose", true); + } + if (cli.hasOption("a") || cli.hasOption("aapt")) { + mAaptPath = cli.getOptionValue("a"); + } + if (cli.hasOption("c") || cli.hasOption("copy-original")) { + flags.put("copyOriginal", true); + } + if (cli.hasOption("p") || cli.hasOption("frame-path")) { + instance.setFrameworkFolder(cli.getOptionValue("p")); + } + if (cli.hasOption("o") || cli.hasOption("output")) { + outFile = new File(cli.getOptionValue("o")); + } else { + outFile = null; + } - Handler handler = new ConsoleHandler(); - logger.addHandler(handler); + // try and build apk + instance.build(new File(appDirName), outFile, flags,mAaptPath); + } - if (verbosity == Verbosity.VERBOSE) { - handler.setLevel(Level.ALL); - logger.setLevel(Level.ALL); - } else { - handler.setFormatter(new Formatter() { - @Override - public String format(LogRecord record) { - return record.getLevel().toString().charAt(0) + ": " - + record.getMessage() - + System.getProperty("line.separator"); - } - }); - } - } + private static void cmdInstallFramework(CommandLine cli) + throws AndrolibException { + int paraCount = cli.getArgList().size(); + String apkName = (String) cli.getArgList().get(paraCount - 1); + String tag = null; + String frame_path = null; - private static enum Verbosity { - NORMAL, VERBOSE, QUIET; - } + if (cli.hasOption("p") || cli.hasOption("frame-path")) { + frame_path = cli.getOptionValue("p"); + } + if (cli.hasOption("t") || cli.hasOption("tag")) { + tag = cli.getOptionValue("t"); + } + new Androlib().installFramework(new File(apkName), tag, frame_path); + } - private static boolean Advanced = false; + private static void cmdPublicizeResources(CommandLine cli) + throws AndrolibException { + int paraCount = cli.getArgList().size(); + String apkName = (String) cli.getArgList().get(paraCount - 1); - static class InvalidArgsError extends AndrolibException { + new Androlib().publicizeResources(new File(apkName)); + } - } + private static void _version() { + System.out.println(Androlib.getVersion()); + } + + @SuppressWarnings("static-access") + private static void _Options() { + + // create options + Option versionOption = OptionBuilder.withLongOpt("version") + .withDescription("prints the version then exits") + .create("version"); + + Option advanceOption = OptionBuilder.withLongOpt("advanced") + .withDescription("prints advance information.") + .create("advance"); + + Option noSrcOption = OptionBuilder.withLongOpt("no-src") + .withDescription("Do not decode sources.") + .create("s"); + + Option noResOption = OptionBuilder.withLongOpt("no-res") + .withDescription("Do not decode resources.") + .create("r"); + + Option debugDecOption = OptionBuilder.withLongOpt("debug") + .withDescription("Decode in debug mode. Check project page for more info.") + .create("d"); + + Option analysisOption = OptionBuilder.withLongOpt("match-original") + .withDescription("Keeps files to closest to original as possible. Prevents rebuild.") + .create("m"); + + Option debugLinePrefix = OptionBuilder.withLongOpt("debug-line-prefix") + .withDescription("Smali line prefix when decoding in debug mode. Default is \"a=0;// \".") + .hasArg(true) + .withArgName("prefix") + .create(); + + Option apiLevelOption = OptionBuilder.withLongOpt("api") + .withDescription("The numeric api-level of the file to generate, e.g. 14 for ICS.") + .hasArg(true) + .withArgName("API") + .create(); + + Option debugBuiOption = OptionBuilder.withLongOpt("debug") + .withDescription("Builds in debug mode. Check project page for more info.") + .create("d"); + + Option noDbgOption = OptionBuilder.withLongOpt("no-debug-info") + .withDescription("don't write out debug info (.local, .param, .line, etc.)") + .create("b"); + + Option forceDecOption = OptionBuilder.withLongOpt("force") + .withDescription("Force delete destination directory.") + .create("f"); + + Option frameTagOption = OptionBuilder.withLongOpt("frame-tag") + .withDescription("Uses framework files tagged by .") + .hasArg(true) + .withArgName("tag") + .create("t"); + + Option frameDirOption = OptionBuilder.withLongOpt("frame-path") + .withDescription("Uses framework files located in .") + .hasArg(true) + .withArgName("dir") + .create("p"); + + Option frameIfDirOption = OptionBuilder.withLongOpt("frame-path") + .withDescription("Stores framework files into .") + .hasArg(true) + .withArgName("dir") + .create("p"); + + Option keepResOption = OptionBuilder.withLongOpt("keep-broken-res") + .withDescription("Use if there was an error and some resources were dropped, e.g.\n" + + " \"Invalid config flags detected. Dropping resources\", but you\n" + + " want to decode them anyway, even with errors. You will have to\n" + + " fix them manually before building.") + .create("k"); + + Option forceBuiOption = OptionBuilder.withLongOpt("force-all") + .withDescription("Skip changes detection and build all files.") + .create("f"); + + Option aaptOption = OptionBuilder.withLongOpt("aapt") + .hasArg(true) + .withArgName("loc") + .withDescription("Loads aapt from specified location.") + .create("a"); + + Option originalOption = OptionBuilder.withLongOpt("copy-original") + .withDescription("Copies original AndroidManifest.xml and META-INF. See project page for more info.") + .create("c"); + + Option tagOption = OptionBuilder.withLongOpt("tag") + .withDescription("Tag frameworks using .") + .hasArg(true) + .withArgName("tag") + .create("t"); + + Option outputBuiOption = OptionBuilder.withLongOpt("output") + .withDescription("The name of apk that gets written. Default is dist/name.apk") + .hasArg(true) + .withArgName("dir") + .create("o"); + + Option outputDecOption = OptionBuilder.withLongOpt("output") + .withDescription("The name of folder that gets written. Default is apk.out") + .hasArg(true) + .withArgName("dir") + .create("o"); + + Option quietOption = OptionBuilder.withLongOpt("quiet") + .create("q"); + + Option verboseOption = OptionBuilder.withLongOpt("verbose") + .create("v"); + + // check for advance mode + if (isAdvanceMode()) { + DecodeOptions.addOption(debugLinePrefix); + DecodeOptions.addOption(debugDecOption); + DecodeOptions.addOption(noDbgOption); + DecodeOptions.addOption(keepResOption); + DecodeOptions.addOption(analysisOption); + DecodeOptions.addOption(apiLevelOption); + + BuildOptions.addOption(debugBuiOption); + BuildOptions.addOption(aaptOption); + BuildOptions.addOption(originalOption); + } + + // add global options + normalOptions.addOption(versionOption); + normalOptions.addOption(advanceOption); + + // add basic decode options + DecodeOptions.addOption(frameTagOption); + DecodeOptions.addOption(outputDecOption); + DecodeOptions.addOption(frameDirOption); + DecodeOptions.addOption(forceDecOption); + DecodeOptions.addOption(noSrcOption); + DecodeOptions.addOption(noResOption); + + // add basic build options + BuildOptions.addOption(outputBuiOption); + BuildOptions.addOption(frameDirOption); + BuildOptions.addOption(forceBuiOption); + + // add basic framework options + frameOptions.addOption(tagOption); + frameOptions.addOption(frameIfDirOption); + + // add all, loop existing cats then manually add advance + for (Object op : normalOptions.getOptions()) { + allOptions.addOption((Option)op); + } + for (Object op : DecodeOptions.getOptions()) { + allOptions.addOption((Option)op); + } + for (Object op : BuildOptions.getOptions()) { + allOptions.addOption((Option)op); + } + for (Object op : frameOptions.getOptions()) { + allOptions.addOption((Option)op); + } + allOptions.addOption(analysisOption); + allOptions.addOption(debugLinePrefix); + allOptions.addOption(debugDecOption); + allOptions.addOption(noDbgOption); + allOptions.addOption(keepResOption); + allOptions.addOption(debugBuiOption); + allOptions.addOption(aaptOption); + allOptions.addOption(originalOption); + allOptions.addOption(verboseOption); + allOptions.addOption(quietOption); + } + + private static String verbosityHelp() { + if (isAdvanceMode()) { + return "[-q|--quiet OR -v|--verbose] "; + } else { + return ""; + } + } + + private static void usage(CommandLine commandLine) { + + // load basicOptions + _Options(); + HelpFormatter formatter = new HelpFormatter(); + formatter.setWidth(120); + + // print out license info prior to formatter. + System.out.println( + "Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" + + "with smali v" + ApktoolProperties.get("smaliVersion") + + " and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" + + "Copyright 2010 Ryszard Wiśniewski \n" + + "Updated by Connor Tumbleson " ); + if (isAdvanceMode()) { + System.out.println("Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n"); + }else { + System.out.println(""); + } + + // 4 usage outputs (general, frameworks, decode, build) + formatter.printHelp("apktool " + verbosityHelp(), normalOptions); + formatter.printHelp("apktool " + verbosityHelp() + "if|install-framework [options] ", frameOptions); + formatter.printHelp("apktool " + verbosityHelp() + "d[ecode] [options] ", DecodeOptions); + formatter.printHelp("apktool " + verbosityHelp() + "b[uild] [options] ", BuildOptions); + if (isAdvanceMode()) { + formatter.printHelp("apktool " + verbosityHelp() + "publicize-resources ", + "Make all framework resources public.", emptyOptions, null); + } else { + System.out.println(""); + } + + // print out more information + System.out.println( + "For additional info, see: http://code.google.com/p/android-apktool/ \n" + + "For smali/baksmali info, see: http://code.google.com/p/smali/"); + } + + private static void setupLogging(Verbosity verbosity) { + Logger logger = Logger.getLogger(""); + for (Handler handler : logger.getHandlers()) { + logger.removeHandler(handler); + } + LogManager.getLogManager().reset(); + + if (verbosity == Verbosity.QUIET) { + return; + } + + Handler handler = new Handler(){ + @Override + public void publish(LogRecord record) { + if (getFormatter() == null) { + setFormatter(new SimpleFormatter()); + } + + try { + String message = getFormatter().format(record); + if (record.getLevel().intValue() >= Level.WARNING.intValue()) { + System.err.write(message.getBytes()); + } else { + System.out.write(message.getBytes()); + } + } catch (Exception exception) { + reportError(null, exception, ErrorManager.FORMAT_FAILURE); + } + } + @Override + public void close() throws SecurityException {} + @Override + public void flush(){} + }; + + logger.addHandler(handler); + + if (verbosity == Verbosity.VERBOSE) { + handler.setLevel(Level.ALL); + logger.setLevel(Level.ALL); + } else { + handler.setFormatter(new Formatter() { + @Override + public String format(LogRecord record) { + return record.getLevel().toString().charAt(0) + ": " + + record.getMessage() + + System.getProperty("line.separator"); + } + }); + } + } + + public static boolean isAdvanceMode() { + return advanceMode; + } + + public static void setAdvanceMode(boolean advanceMode) { + Main.advanceMode = advanceMode; + } + + private static enum Verbosity { + NORMAL, VERBOSE, QUIET; + } + + private static boolean advanceMode = false; + + private final static Options normalOptions; + private final static Options DecodeOptions; + private final static Options BuildOptions; + private final static Options frameOptions; + private final static Options allOptions; + private final static Options emptyOptions; + + static { + //normal and advance usage output + normalOptions = new Options(); + BuildOptions = new Options(); + DecodeOptions = new Options(); + frameOptions = new Options(); + allOptions = new Options(); + emptyOptions = new Options(); + } } diff --git a/brut.apktool/apktool-lib/build.gradle b/brut.apktool/apktool-lib/build.gradle index 8ec0898c..9ae3d0b5 100644 --- a/brut.apktool/apktool-lib/build.gradle +++ b/brut.apktool/apktool-lib/build.gradle @@ -16,26 +16,11 @@ import org.apache.tools.ant.filters.* -// kudos #smali - JesusFreke, for how to build release task -if (!('release' in gradle.startParameter.taskNames)) { - ant.loadfile(srcFile: "../../.git/refs/heads/master", property: fullrev, failonerror: false); - if (ant.properties[fullrev] == null) { - gitrev_version = "SNAPSHOT_DEV"; - println "Building SNAPSHOT (no .git folder found)"; - } else { - gitrev_version = ant.properties[fullrev].substring(0,10); - println "Building SNAPSHOT: " + gitrev_version; - } -} else { - gitrev_version = ''; - println "Building RELEASE: " + apktoolversion; -} - processResources { from('src/main/resources/properties') { include '**/*.properties' into 'properties' - filter(ReplaceTokens, tokens: [version: apktoolversion, gitrev: gitrev_version] ) + filter(ReplaceTokens, tokens: [version: project.apktool_version, gitrev: project.hash] ) } from('src/main/resources/') { include '**/*.jar' @@ -43,19 +28,20 @@ processResources { includeEmptyDirs = false } + dependencies { compile ("junit:junit:4.10") { exclude(module: 'hamcrest-core') } + compile project(':brut.j.dir'), project(':brut.j.util'), project(':brut.j.common'), project(':brut.apktool.smali:util'), - project(':brut.apktool.smali:dexlib'), + project(':brut.apktool.smali:dexlib2'), project(':brut.apktool.smali:baksmali'), project(':brut.apktool.smali:smali'), - "org.yaml:snakeyaml:1.11", - "net.sf.kxml:kxml2:2.3.0", + "org.yaml:snakeyaml:1.12", "xpp3:xpp3:1.1.4c", "xmlunit:xmlunit:1.3", "com.google.guava:guava:12.0", diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index e619c162..a566e1b5 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -16,11 +16,12 @@ package brut.androlib; -import brut.androlib.err.InFileNotFoundException; import brut.androlib.java.AndrolibJava; import brut.androlib.res.AndrolibResources; import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResTable; +import brut.androlib.res.data.ResUnknownFiles; +import brut.androlib.res.decoder.ResFileDecoder; import brut.androlib.res.util.ExtFile; import brut.androlib.src.SmaliBuilder; import brut.androlib.src.SmaliDecoder; @@ -29,10 +30,14 @@ import brut.directory.*; import brut.util.BrutIO; import brut.util.OS; import java.io.*; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.*; +import java.nio.file.Path; +import java.util.*; import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.nio.file.Files; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; @@ -40,518 +45,686 @@ import org.yaml.snakeyaml.Yaml; * @author Ryszard Wiśniewski */ public class Androlib { - private final AndrolibResources mAndRes = new AndrolibResources(); + private final AndrolibResources mAndRes = new AndrolibResources(); + protected final ResUnknownFiles mResUnknownFiles = new ResUnknownFiles(); - public ResTable getResTable(ExtFile apkFile) throws AndrolibException { - return mAndRes.getResTable(apkFile, true); - } + public ResTable getResTable(ExtFile apkFile) throws AndrolibException { + return mAndRes.getResTable(apkFile, true); + } - public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) - throws AndrolibException { - return mAndRes.getResTable(apkFile, loadMainPkg); - } + public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) + throws AndrolibException { + return mAndRes.getResTable(apkFile, loadMainPkg); + } - public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug) - throws AndrolibException { - try { - Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw classes.dex file..."); - apkFile.getDirectory().copyToDir(outDir, "classes.dex"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug) + throws AndrolibException { + try { + Directory apk = apkFile.getDirectory(); + LOGGER.info("Copying raw classes.dex file..."); + apkFile.getDirectory().copyToDir(outDir, "classes.dex"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - public void decodeSourcesSmali(File apkFile, File outDir, boolean debug, - boolean bakdeb) throws AndrolibException { - try { - File smaliDir = new File(outDir, SMALI_DIRNAME); - OS.rmdir(smaliDir); - smaliDir.mkdirs(); - LOGGER.info("Baksmaling..."); - SmaliDecoder.decode(apkFile, smaliDir, debug, bakdeb); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } + public void decodeSourcesSmali(File apkFile, File outDir, boolean debug, String debugLinePrefix, + boolean bakdeb, int api) throws AndrolibException { + try { + File smaliDir = new File(outDir, SMALI_DIRNAME); + OS.rmdir(smaliDir); + smaliDir.mkdirs(); + LOGGER.info("Baksmaling..."); + SmaliDecoder.decode(apkFile, smaliDir, debug, debugLinePrefix, bakdeb, api); + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + } - public void decodeSourcesJava(ExtFile apkFile, File outDir, boolean debug) - throws AndrolibException { - LOGGER.info("Decoding Java sources..."); - new AndrolibJava().decode(apkFile, outDir); - } + public void decodeSourcesJava(ExtFile apkFile, File outDir, boolean debug) + throws AndrolibException { + LOGGER.info("Decoding Java sources..."); + new AndrolibJava().decode(apkFile, outDir); + } - public void decodeManifestRaw(ExtFile apkFile, File outDir) - throws AndrolibException { - try { - Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw manifest..."); - apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public void decodeManifestRaw(ExtFile apkFile, File outDir) + throws AndrolibException { + try { + Directory apk = apkFile.getDirectory(); + LOGGER.info("Copying raw manifest..."); + apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - public void decodeManifestFull(ExtFile apkFile, File outDir, - ResTable resTable) throws AndrolibException { - mAndRes.decodeManifest(resTable, apkFile, outDir); - } + public void decodeManifestFull(ExtFile apkFile, File outDir, + ResTable resTable) throws AndrolibException { + mAndRes.decodeManifest(resTable, apkFile, outDir); + } - public void decodeResourcesRaw(ExtFile apkFile, File outDir) - throws AndrolibException { - try { - // Directory apk = apkFile.getDirectory(); - LOGGER.info("Copying raw resources..."); - apkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public void decodeResourcesRaw(ExtFile apkFile, File outDir) + throws AndrolibException { + try { + // Directory apk = apkFile.getDirectory(); + LOGGER.info("Copying raw resources..."); + apkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - public void decodeResourcesFull(ExtFile apkFile, File outDir, - ResTable resTable) throws AndrolibException { - mAndRes.decode(resTable, apkFile, outDir); - } + public void decodeResourcesFull(ExtFile apkFile, File outDir, + ResTable resTable) throws AndrolibException { + mAndRes.decode(resTable, apkFile, outDir); + } - public void decodeRawFiles(ExtFile apkFile, File outDir) - throws AndrolibException { - LOGGER.info("Copying assets and libs..."); - try { - Directory in = apkFile.getDirectory(); - if (in.containsDir("assets")) { - in.copyToDir(outDir, "assets"); - } - if (in.containsDir("lib")) { - in.copyToDir(outDir, "lib"); - } - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public void decodeRawFiles(ExtFile apkFile, File outDir) + throws AndrolibException { + LOGGER.info("Copying assets and libs..."); + try { + Directory in = apkFile.getDirectory(); + if (in.containsDir("assets")) { + in.copyToDir(outDir, "assets"); + } + if (in.containsDir("lib")) { + in.copyToDir(outDir, "lib"); + } + if (in.containsDir("libs")) { + in.copyToDir(outDir, "libs"); + } + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - public void writeMetaFile(File mOutDir, Map meta) - throws AndrolibException { - DumperOptions options = new DumperOptions(); - options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); - // options.setIndent(4); - Yaml yaml = new Yaml(options); + private boolean isAPKFileNames(String file) { + for (String apkFile : APK_STANDARD_ALL_FILENAMES) { + if (apkFile.equals(file) || file.startsWith(apkFile + "/")) { + return true; + } + } + return false; + } - FileWriter writer = null; - try { - writer = new FileWriter(new File(mOutDir, "apktool.yml")); - yaml.dump(meta, writer); - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException ex) { - } - } - } - } + public void decodeUnknownFiles(ExtFile apkFile, File outDir, ResTable resTable) + throws AndrolibException { + LOGGER.info("Copying unknown files..."); + File unknownOut = new File(outDir, UNK_DIRNAME); + ZipEntry invZipFile; - public Map readMetaFile(ExtFile appDir) - throws AndrolibException { - InputStream in = null; - try { - in = appDir.getDirectory().getFileInput("apktool.yml"); - Yaml yaml = new Yaml(); - return (Map) yaml.load(in); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) { - } - } - } - } + // have to use container of ZipFile to help identify compression type + // with regular looping of apkFile for easy copy + try { + Directory unk = apkFile.getDirectory(); + ZipExtFile apkZipFile = new ZipExtFile(apkFile.getAbsolutePath()); - public void build(File appDir, File outFile, - HashMap flags, ExtFile origApk, String aaptPath) - throws BrutException { - build(new ExtFile(appDir), outFile, flags, origApk, aaptPath); - } + // loop all items in container recursively, ignoring any that are pre-defined by aapt + Set files = unk.getFiles(true); + for (String file : files) { + if (!isAPKFileNames(file)) { - public void build(ExtFile appDir, File outFile, - HashMap flags, ExtFile origApk, String aaptPath) - throws BrutException { + // copy file out of archive into special "unknown" folder + // to be re-included on build + unk.copyToDir(unknownOut,file); + try { + // ignore encryption + apkZipFile.getEntry(file.toString()).getGeneralPurposeBit().useEncryption(false); + invZipFile = apkZipFile.getEntry(file.toString()); - mAaptPath = aaptPath; - Map meta = readMetaFile(appDir); - Object t1 = meta.get("isFrameworkApk"); - flags.put("framework", t1 == null ? false : (Boolean) t1); - flags.put("compression", meta.get("compressionType") == null ? false - : (Boolean) meta.get("compressionType")); - mAndRes.setSdkInfo((Map) meta.get("sdkInfo")); + // lets record the name of the file, and its compression type + // so that we may re-include it the same way + if (invZipFile != null) { + mResUnknownFiles.addUnknownFileInfo(invZipFile.getName(), String.valueOf(invZipFile.getMethod())); + } + } catch (NullPointerException ignored) { - // check the orig apk - if (flags.get("injectOriginal")) { - if (!origApk.isFile() || !origApk.canRead()) { - throw new InFileNotFoundException(); - } else { - mOrigApkFile = origApk; - } - } + } + } + } + apkZipFile.close(); + } + catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + catch (IOException ex) { + throw new AndrolibException(ex); + } + } - if (outFile == null) { - String outFileName = (String) meta.get("apkFileName"); - outFile = new File(appDir, "dist" + File.separator - + (outFileName == null ? "out.apk" : outFileName)); - } + public void writeOriginalFiles(ExtFile apkFile, File outDir) + throws AndrolibException { + LOGGER.info("Copying original files..."); + File originalDir = new File(outDir, "original"); + if (!originalDir.exists()) { + originalDir.mkdirs(); + } - new File(appDir, APK_DIRNAME).mkdirs(); - buildSources(appDir, flags); - buildResources(appDir, flags, - (Map) meta.get("usesFramework")); - buildLib(appDir, flags); - buildApk(appDir, outFile, flags); - } + try { + Directory in = apkFile.getDirectory(); + if(in.containsFile("AndroidManifest.xml")) { + in.copyToDir(originalDir, "AndroidManifest.xml"); + } + if (in.containsDir("META-INF")) { + in.copyToDir(originalDir, "META-INF"); + } + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - public void buildSources(File appDir, HashMap flags) - throws AndrolibException { - if (!buildSourcesRaw(appDir, flags) - && !buildSourcesSmali(appDir, flags) - && !buildSourcesJava(appDir, flags)) { - LOGGER.warning("Could not find sources"); - } - } + public void writeMetaFile(File mOutDir, Map meta) + throws AndrolibException { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + // options.setIndent(4); + Yaml yaml = new Yaml(options); - public boolean buildSourcesRaw(File appDir, HashMap flags) - throws AndrolibException { - try { - File working = new File(appDir, "classes.dex"); - if (!working.exists()) { - return false; - } - File stored = new File(appDir, APK_DIRNAME + "/classes.dex"); - if (flags.get("forceBuildAll") || isModified(working, stored)) { - LOGGER.info("Copying classes.dex file..."); - BrutIO.copyAndClose(new FileInputStream(working), - new FileOutputStream(stored)); - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } + Writer writer = null; + try { + writer = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(new File(mOutDir, "apktool.yml")), "UTF-8")); + yaml.dump(meta, writer); + } catch (IOException ex) { + throw new AndrolibException(ex); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException ex) { + } + } + } + } - public boolean buildSourcesSmali(File appDir, HashMap flags) - throws AndrolibException { - ExtFile smaliDir = new ExtFile(appDir, "smali"); - if (!smaliDir.exists()) { - return false; - } - File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); - if (!flags.get("forceBuildAll")) { - LOGGER.info("Checking whether sources has changed..."); - } - if (flags.get("forceBuildAll") || isModified(smaliDir, dex)) { - LOGGER.info("Smaling..."); - dex.delete(); - SmaliBuilder.build(smaliDir, dex, flags); - } - return true; - } + public Map readMetaFile(ExtFile appDir) + throws AndrolibException { + InputStream in = null; + try { + in = appDir.getDirectory().getFileInput("apktool.yml"); + Yaml yaml = new Yaml(); + return (Map) yaml.load(in); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + } + } + } + } - public boolean buildSourcesJava(File appDir, HashMap flags) - throws AndrolibException { - File javaDir = new File(appDir, "src"); - if (!javaDir.exists()) { - return false; - } - File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); - if (!flags.get("forceBuildAll")) { - LOGGER.info("Checking whether sources has changed..."); - } - if (flags.get("forceBuildAll") || isModified(javaDir, dex)) { - LOGGER.info("Building java sources..."); - dex.delete(); - new AndrolibJava().build(javaDir, dex); - } - return true; - } + public void build(File appDir, File outFile, + HashMap flags, String aaptPath) + throws BrutException { + build(new ExtFile(appDir), outFile, flags, aaptPath); + } - public void buildResources(ExtFile appDir, HashMap flags, - Map usesFramework) throws BrutException { - if (!buildResourcesRaw(appDir, flags) - && !buildResourcesFull(appDir, flags, usesFramework) - && !buildManifest(appDir, flags, usesFramework)) { - LOGGER.warning("Could not find resources"); - } - } + public void build(ExtFile appDir, File outFile, + HashMap flags, String aaptPath) + throws BrutException { - public boolean buildResourcesRaw(ExtFile appDir, - HashMap flags) throws AndrolibException { - try { - if (!new File(appDir, "resources.arsc").exists()) { - return false; - } - File apkDir = new File(appDir, APK_DIRNAME); - if (!flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - if (flags.get("forceBuildAll") - || isModified(newFiles(APK_RESOURCES_FILENAMES, appDir), - newFiles(APK_RESOURCES_FILENAMES, apkDir))) { - LOGGER.info("Copying raw resources..."); - appDir.getDirectory() - .copyToDir(apkDir, APK_RESOURCES_FILENAMES); - } - return true; - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + LOGGER.info("Using Apktool " + Androlib.getVersion() + " on " + appDir.getName()); - public boolean buildResourcesFull(File appDir, - HashMap flags, Map usesFramework) - throws AndrolibException { - try { - if (!new File(appDir, "res").exists()) { - return false; - } - if (!flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - File apkDir = new File(appDir, APK_DIRNAME); - if (flags.get("forceBuildAll") - || isModified(newFiles(APP_RESOURCES_FILENAMES, appDir), - newFiles(APK_RESOURCES_FILENAMES, apkDir))) { - LOGGER.info("Building resources..."); + mAaptPath = aaptPath; + Map meta = readMetaFile(appDir); + Object t1 = meta.get("isFrameworkApk"); + flags.put("framework", t1 == null ? false : (Boolean) t1); + flags.put("compression", meta.get("compressionType") == null ? false + : Boolean.valueOf(meta.get("compressionType").toString())); + mAndRes.setSdkInfo((Map) meta.get("sdkInfo")); + mAndRes.setPackageId((Map) meta.get("packageInfo")); + mAndRes.setPackageInfo((Map) meta.get("packageInfo")); + mAndRes.setVersionInfo((Map) meta.get("versionInfo")); - File apkFile = File.createTempFile("APKTOOL", null); - apkFile.delete(); + if (outFile == null) { + String outFileName = (String) meta.get("apkFileName"); + outFile = new File(appDir, "dist" + File.separator + + (outFileName == null ? "out.apk" : outFileName)); + } - File ninePatch = new File(appDir, "9patch"); - if (!ninePatch.exists()) { - ninePatch = null; - } - mAndRes.aaptPackage(apkFile, new File(appDir, - "AndroidManifest.xml"), new File(appDir, "res"), - ninePatch, null, parseUsesFramework(usesFramework), - flags, mAaptPath); + new File(appDir, APK_DIRNAME).mkdirs(); + buildSources(appDir, flags); + buildResources(appDir, flags, + (Map) meta.get("usesFramework")); + buildLib(appDir, flags); + buildCopyOriginalFiles(appDir, flags); + buildApk(appDir, outFile, flags); - Directory tmpDir = new ExtFile(apkFile).getDirectory(); - tmpDir.copyToDir(apkDir, - tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES - : APK_RESOURCES_WITHOUT_RES_FILENAMES); + // we must go after the Apk is built, and copy the files in via Zip + // this is because Aapt won't add files it doesn't know (ex unknown files) + buildUnknownFiles(appDir,outFile,meta); + } - // delete tmpDir - apkFile.delete(); - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } + public void buildSources(File appDir, HashMap flags) + throws AndrolibException { + if (!buildSourcesRaw(appDir, flags) + && !buildSourcesSmali(appDir, flags) + && !buildSourcesJava(appDir, flags)) { + LOGGER.warning("Could not find sources"); + } + } - public boolean buildManifestRaw(ExtFile appDir, - HashMap flags) throws AndrolibException { - try { - File apkDir = new File(appDir, APK_DIRNAME); - LOGGER.info("Copying raw AndroidManifest.xml..."); - appDir.getDirectory().copyToDir(apkDir, APK_MANIFEST_FILENAMES); - return true; - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public boolean buildSourcesRaw(File appDir, HashMap flags) + throws AndrolibException { + try { + File working = new File(appDir, "classes.dex"); + if (!working.exists()) { + return false; + } + File stored = new File(appDir, APK_DIRNAME + "/classes.dex"); + if (flags.get("forceBuildAll") || isModified(working, stored)) { + LOGGER.info("Copying classes.dex file..."); + BrutIO.copyAndClose(new FileInputStream(working), + new FileOutputStream(stored)); + } + return true; + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } - public boolean buildManifest(ExtFile appDir, - HashMap flags, Map usesFramework) - throws BrutException { - try { - if (!new File(appDir, "AndroidManifest.xml").exists()) { - return false; - } - if (!flags.get("forceBuildAll")) { - LOGGER.info("Checking whether resources has changed..."); - } - - File apkDir = new File(appDir, APK_DIRNAME); - - if (flags.get("debug")) { - mAndRes.remove_application_debug(new File(apkDir,"AndroidManifest.xml").getAbsolutePath()); - } + public boolean buildSourcesSmali(File appDir, HashMap flags) + throws AndrolibException { + ExtFile smaliDir = new ExtFile(appDir, "smali"); + if (!smaliDir.exists()) { + return false; + } + File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether sources has changed..."); + } + if (flags.get("forceBuildAll") || isModified(smaliDir, dex)) { + LOGGER.info("Smaling..."); + dex.delete(); + SmaliBuilder.build(smaliDir, dex, flags); + } + return true; + } - if (flags.get("forceBuildAll") - || isModified(newFiles(APK_MANIFEST_FILENAMES, appDir), - newFiles(APK_MANIFEST_FILENAMES, apkDir))) { - LOGGER.info("Building AndroidManifest.xml..."); + public boolean buildSourcesJava(File appDir, HashMap flags) + throws AndrolibException { + File javaDir = new File(appDir, "src"); + if (!javaDir.exists()) { + return false; + } + File dex = new File(appDir, APK_DIRNAME + "/classes.dex"); + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether sources has changed..."); + } + if (flags.get("forceBuildAll") || isModified(javaDir, dex)) { + LOGGER.info("Building java sources..."); + dex.delete(); + new AndrolibJava().build(javaDir, dex); + } + return true; + } - File apkFile = File.createTempFile("APKTOOL", null); - apkFile.delete(); + public void buildResources(ExtFile appDir, HashMap flags, + Map usesFramework) throws BrutException { + if (!buildResourcesRaw(appDir, flags) + && !buildResourcesFull(appDir, flags, usesFramework) + && !buildManifest(appDir, flags, usesFramework)) { + LOGGER.warning("Could not find resources"); + } + } - File ninePatch = new File(appDir, "9patch"); - if (!ninePatch.exists()) { - ninePatch = null; - } + public boolean buildResourcesRaw(ExtFile appDir, + HashMap flags) throws AndrolibException { + try { + if (!new File(appDir, "resources.arsc").exists()) { + return false; + } + File apkDir = new File(appDir, APK_DIRNAME); + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether resources has changed..."); + } + if (flags.get("forceBuildAll") + || isModified(newFiles(APK_RESOURCES_FILENAMES, appDir), + newFiles(APK_RESOURCES_FILENAMES, apkDir))) { + LOGGER.info("Copying raw resources..."); + appDir.getDirectory() + .copyToDir(apkDir, APK_RESOURCES_FILENAMES); + } + return true; + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - mAndRes.aaptPackage(apkFile, new File(appDir, - "AndroidManifest.xml"), null, ninePatch, null, - parseUsesFramework(usesFramework), flags, mAaptPath); + public boolean buildResourcesFull(File appDir, + HashMap flags, Map usesFramework) + throws AndrolibException { + try { + if (!new File(appDir, "res").exists()) { + return false; + } + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether resources has changed..."); + } + File apkDir = new File(appDir, APK_DIRNAME); + if (flags.get("forceBuildAll") + || isModified(newFiles(APP_RESOURCES_FILENAMES, appDir), + newFiles(APK_RESOURCES_FILENAMES, apkDir))) { + LOGGER.info("Building resources..."); - Directory tmpDir = new ExtFile(apkFile).getDirectory(); - tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES); + File apkFile = File.createTempFile("APKTOOL", null); + apkFile.delete(); - } - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } catch (AndrolibException ex) { - LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file."); - return buildManifestRaw(appDir, flags); - } - } + File ninePatch = new File(appDir, "9patch"); + if (!ninePatch.exists()) { + ninePatch = null; + } + mAndRes.aaptPackage(apkFile, new File(appDir, + "AndroidManifest.xml"), new File(appDir, "res"), + ninePatch, null, parseUsesFramework(usesFramework), + flags, mAaptPath); - public void buildLib(File appDir, HashMap flags) - throws AndrolibException { - File working = new File(appDir, "lib"); - if (!working.exists()) { - return; - } - File stored = new File(appDir, APK_DIRNAME + "/lib"); - if (flags.get("forceBuildAll") || isModified(working, stored)) { - LOGGER.info("Copying libs..."); - try { - OS.rmdir(stored); - OS.cpdir(working, stored); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - } + Directory tmpDir = new ExtFile(apkFile).getDirectory(); + tmpDir.copyToDir(apkDir, + tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES + : APK_RESOURCES_WITHOUT_RES_FILENAMES); - public void buildApk(File appDir, File outApk, - HashMap flags) throws AndrolibException { - LOGGER.info("Building apk file..."); - if (outApk.exists()) { - outApk.delete(); - } else { - File outDir = outApk.getParentFile(); - if (outDir != null && !outDir.exists()) { - outDir.mkdirs(); - } - } - File assetDir = new File(appDir, "assets"); - if (!assetDir.exists()) { - assetDir = null; - } - mAndRes.aaptPackage(outApk, null, null, new File(appDir, APK_DIRNAME), - assetDir, null, flags, mAaptPath); + // delete tmpDir + apkFile.delete(); + } + return true; + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + } - // retain signature - // aapt r (remove) - // aapt a (add) - if (flags.get("injectOriginal")) { - LOGGER.info("Injecting contents isn't ready yet. Sorry :("); - } - } + public boolean buildManifestRaw(ExtFile appDir, + HashMap flags) throws AndrolibException { + try { + File apkDir = new File(appDir, APK_DIRNAME); + LOGGER.info("Copying raw AndroidManifest.xml..."); + appDir.getDirectory().copyToDir(apkDir, APK_MANIFEST_FILENAMES); + return true; + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - public void publicizeResources(File arscFile) throws AndrolibException { - mAndRes.publicizeResources(arscFile); - } + public boolean buildManifest(ExtFile appDir, + HashMap flags, Map usesFramework) + throws BrutException { + try { + if (!new File(appDir, "AndroidManifest.xml").exists()) { + return false; + } + if (!flags.get("forceBuildAll")) { + LOGGER.info("Checking whether resources has changed..."); + } - public void installFramework(File frameFile, String tag, String frame_path) - throws AndrolibException { - mAndRes.setFrameworkFolder(frame_path); - mAndRes.installFramework(frameFile, tag); - } + File apkDir = new File(appDir, APK_DIRNAME); - public boolean isFrameworkApk(ResTable resTable) { - for (ResPackage pkg : resTable.listMainPackages()) { - if (pkg.getId() < 64) { - return true; - } - } - return false; - } + if (flags.get("debug")) { + mAndRes.remove_application_debug(new File(apkDir,"AndroidManifest.xml").getAbsolutePath()); + } - public static String getVersion() { - String version = ApktoolProperties.get("application.version"); - return version.endsWith("-SNAPSHOT") ? version.substring(0, - version.length() - 9) - + '.' + ApktoolProperties.get("git.commit.id.abbrev") : version; - } + if (flags.get("forceBuildAll") + || isModified(newFiles(APK_MANIFEST_FILENAMES, appDir), + newFiles(APK_MANIFEST_FILENAMES, apkDir))) { + LOGGER.info("Building AndroidManifest.xml..."); - private File[] parseUsesFramework(Map usesFramework) - throws AndrolibException { - if (usesFramework == null) { - return null; - } + File apkFile = File.createTempFile("APKTOOL", null); + apkFile.delete(); - List ids = (List) usesFramework.get("ids"); - if (ids == null || ids.isEmpty()) { - return null; - } + File ninePatch = new File(appDir, "9patch"); + if (!ninePatch.exists()) { + ninePatch = null; + } - String tag = (String) usesFramework.get("tag"); - File[] files = new File[ids.size()]; - int i = 0; - for (int id : ids) { - files[i++] = mAndRes.getFrameworkApk(id, tag); - } - return files; - } + mAndRes.aaptPackage(apkFile, new File(appDir, + "AndroidManifest.xml"), null, ninePatch, null, + parseUsesFramework(usesFramework), flags, mAaptPath); - private boolean isModified(File working, File stored) { - if (!stored.exists()) { - return true; - } - return BrutIO.recursiveModifiedTime(working) > BrutIO - .recursiveModifiedTime(stored); - } + Directory tmpDir = new ExtFile(apkFile).getDirectory(); + tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES); - private boolean isModified(File[] working, File[] stored) { - for (int i = 0; i < stored.length; i++) { - if (!stored[i].exists()) { - return true; - } - } - return BrutIO.recursiveModifiedTime(working) > BrutIO - .recursiveModifiedTime(stored); - } + } + return true; + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } catch (AndrolibException ex) { + LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file."); + return buildManifestRaw(appDir, flags); + } + } - private File[] newFiles(String[] names, File dir) { - File[] files = new File[names.length]; - for (int i = 0; i < names.length; i++) { - files[i] = new File(dir, names[i]); - } - return files; - } + public void buildLib(File appDir, HashMap flags) + throws AndrolibException { + File working = new File(appDir, "lib"); + if (!working.exists()) { + return; + } + File stored = new File(appDir, APK_DIRNAME + "/lib"); + if (flags.get("forceBuildAll") || isModified(working, stored)) { + LOGGER.info("Copying libs..."); + try { + OS.rmdir(stored); + OS.cpdir(working, stored); + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + } + } - public void setApkFile(File apkFile) { - mOrigApkFile = new ExtFile(apkFile); - } - - public void setFrameworkFolder(String path) { - mAndRes.setFrameworkFolder(path); - } + public void buildCopyOriginalFiles(File appDir, + HashMap flags) throws AndrolibException { + if (flags.get("copyOriginal")) { + File originalDir = new File(appDir, "original"); + if(originalDir.exists()) { + try { + LOGGER.info("Copy original files..."); + Directory in = (new ExtFile(originalDir)).getDirectory(); + if(in.containsFile("AndroidManifest.xml")) { + LOGGER.info("Copy AndroidManifest.xml..."); + in.copyToDir(new File(appDir, APK_DIRNAME), "AndroidManifest.xml"); + } + if (in.containsDir("META-INF")) { + LOGGER.info("Copy META-INF..."); + in.copyToDir(new File(appDir, APK_DIRNAME), "META-INF"); + } + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + } + } - private ExtFile mOrigApkFile = null; - private String mAaptPath = null; + public void buildUnknownFiles(File appDir, File outFile, Map meta) + throws AndrolibException { + File file; + mPath = Paths.get(appDir.getPath() + File.separatorChar + UNK_DIRNAME); - private final static Logger LOGGER = Logger.getLogger(Androlib.class - .getName()); + if (meta.containsKey("unknownFiles")) { + LOGGER.info("Copying unknown files/dir..."); - private final static String SMALI_DIRNAME = "smali"; - private final static String APK_DIRNAME = "build/apk"; - private final static String[] APK_RESOURCES_FILENAMES = new String[] { - "resources.arsc", "AndroidManifest.xml", "res" }; - private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] { - "resources.arsc", "AndroidManifest.xml" }; - private final static String[] APP_RESOURCES_FILENAMES = new String[] { - "AndroidManifest.xml", "res" }; - private final static String[] APK_MANIFEST_FILENAMES = new String[] { "AndroidManifest.xml" }; + Map files = (Map)meta.get("unknownFiles"); + + try { + // set our filesystem options + Map zip_properties = new HashMap<>(); + zip_properties.put("create", "false"); + zip_properties.put("encoding", "UTF-8"); + + // create filesystem + Path path = Paths.get(outFile.getAbsolutePath()); + URI apkFileSystem = new URI("jar", outFile.toURI().toString(), null); + + // loop through files inside + for (Map.Entry entry : files.entrySet()) { + + file = new File(mPath.toFile(), entry.getKey()); + if (file.isFile() && file.exists()) { + insertFolder(apkFileSystem, zip_properties, file.getParentFile(), entry.getValue(), mPath.toAbsolutePath()); + insertFile(apkFileSystem, zip_properties, file, entry.getValue(), mPath.toAbsolutePath()); + } + } + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (URISyntaxException ex) { + throw new AndrolibException(ex); + } + } + } + + private void insertFile(URI apkFileSystem, Map zip_properties, File insert, String method, Path location) + throws AndrolibException, IOException { + + // ZipFileSystem only writes at .close() + // http://mail.openjdk.java.net/pipermail/nio-dev/2012-July/001764.html + try(FileSystem fs = FileSystems.newFileSystem(apkFileSystem, zip_properties)) { + + Path root = fs.getPath("/"); + + // in order to get the path relative to the zip, we strip off the absolute path, minus what we + // already have in the zip. thus /var/files/apktool/apk/unknown/folder/file => /folder/file + Path dest = fs.getPath(root.toString(), insert.getAbsolutePath().replace(location.toString(),"")); + Path newFile = Paths.get(insert.getAbsolutePath()); + Files.copy(newFile,dest, StandardCopyOption.REPLACE_EXISTING); + fs.close(); + } + } + + private void insertFolder(URI apkFileSystem, Map zip_properties, File insert, String method, Path location) + throws AndrolibException, IOException { + + try(FileSystem fs = FileSystems.newFileSystem(apkFileSystem, zip_properties)) { + + Path root = fs.getPath("/"); + Path dest = fs.getPath(root.toString(), insert.getAbsolutePath().replace(location.toString(),"")); + Path parent = dest.normalize(); + + // check for folder existing in apkFileSystem + if (parent != null && Files.notExists(parent)) { + if (!Files.isDirectory(parent, LinkOption.NOFOLLOW_LINKS)) { + Files.createDirectories(parent); + } + } + fs.close(); + } + } + + public void buildApk(File appDir, File outApk, + HashMap flags) throws AndrolibException { + LOGGER.info("Building apk file..."); + if (outApk.exists()) { + outApk.delete(); + } else { + File outDir = outApk.getParentFile(); + if (outDir != null && !outDir.exists()) { + outDir.mkdirs(); + } + } + File assetDir = new File(appDir, "assets"); + if (!assetDir.exists()) { + assetDir = null; + } + mAndRes.aaptPackage(outApk, null, null, new File(appDir, APK_DIRNAME), + assetDir, null, flags, mAaptPath); + } + + public void publicizeResources(File arscFile) throws AndrolibException { + mAndRes.publicizeResources(arscFile); + } + + public void installFramework(File frameFile, String tag, String frame_path) + throws AndrolibException { + mAndRes.setFrameworkFolder(frame_path); + mAndRes.installFramework(frameFile, tag); + } + + public boolean isFrameworkApk(ResTable resTable) { + for (ResPackage pkg : resTable.listMainPackages()) { + if (pkg.getId() < 64) { + return true; + } + } + return false; + } + + public static String getVersion() { + return ApktoolProperties.get("application.version"); + } + + private File[] parseUsesFramework(Map usesFramework) + throws AndrolibException { + if (usesFramework == null) { + return null; + } + + List ids = (List) usesFramework.get("ids"); + if (ids == null || ids.isEmpty()) { + return null; + } + + String tag = (String) usesFramework.get("tag"); + File[] files = new File[ids.size()]; + int i = 0; + for (int id : ids) { + files[i++] = mAndRes.getFrameworkApk(id, tag); + } + return files; + } + + private boolean isModified(File working, File stored) { + if (!stored.exists()) { + return true; + } + return BrutIO.recursiveModifiedTime(working) > BrutIO + .recursiveModifiedTime(stored); + } + + private boolean isModified(File[] working, File[] stored) { + for (int i = 0; i < stored.length; i++) { + if (!stored[i].exists()) { + return true; + } + } + return BrutIO.recursiveModifiedTime(working) > BrutIO + .recursiveModifiedTime(stored); + } + + private File[] newFiles(String[] names, File dir) { + File[] files = new File[names.length]; + for (int i = 0; i < names.length; i++) { + files[i] = new File(dir, names[i]); + } + return files; + } + + public void setFrameworkFolder(String path) { + mAndRes.setFrameworkFolder(path); + } + + private String mAaptPath = null; + private Path mPath = null; + + private final static Logger LOGGER = Logger.getLogger(Androlib.class + .getName()); + + private final static String SMALI_DIRNAME = "smali"; + private final static String APK_DIRNAME = "build/apk"; + private final static String UNK_DIRNAME = "unknown"; + private final static String[] APK_RESOURCES_FILENAMES = new String[] { + "resources.arsc", "AndroidManifest.xml", "res" }; + private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] { + "resources.arsc", "AndroidManifest.xml" }; + private final static String[] APP_RESOURCES_FILENAMES = new String[] { + "AndroidManifest.xml", "res" }; + private final static String[] APK_MANIFEST_FILENAMES = new String[] { + "AndroidManifest.xml" }; + private final static String[] APK_STANDARD_ALL_FILENAMES = new String[] { + "classes.dex", "AndroidManifest.xml", "resources.arsc","res","lib", "libs","assets","META-INF" }; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/AndrolibException.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/AndrolibException.java index a68e4680..d1601341 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/AndrolibException.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/AndrolibException.java @@ -22,18 +22,18 @@ import brut.common.BrutException; * @author Ryszard Wiśniewski */ public class AndrolibException extends BrutException { - public AndrolibException() { - } + public AndrolibException() { + } - public AndrolibException(String message) { - super(message); - } + public AndrolibException(String message) { + super(message); + } - public AndrolibException(String message, Throwable cause) { - super(message, cause); - } + public AndrolibException(String message, Throwable cause) { + super(message, cause); + } - public AndrolibException(Throwable cause) { - super(cause); - } + public AndrolibException(Throwable cause) { + super(cause); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java index 672b2b72..b69bcaab 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java @@ -21,295 +21,358 @@ import brut.androlib.err.OutDirExistsException; import brut.androlib.res.AndrolibResources; import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResTable; +import brut.androlib.res.data.ResUnknownFiles; import brut.androlib.res.util.ExtFile; import brut.common.BrutException; import brut.directory.DirectoryException; +import brut.directory.ZipExtFile; import brut.util.OS; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; + import java.io.File; import java.io.IOException; import java.util.*; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import java.util.logging.Logger; import java.util.zip.ZipEntry; /** * @author Ryszard Wiśniewski */ public class ApkDecoder { - public ApkDecoder() { - this(new Androlib()); - } + public ApkDecoder() { + this(new Androlib()); + } - public ApkDecoder(Androlib androlib) { - mAndrolib = androlib; - } + public ApkDecoder(Androlib androlib) { + mAndrolib = androlib; + } - public ApkDecoder(File apkFile) { - this(apkFile, new Androlib()); - } + public ApkDecoder(File apkFile) { + this(apkFile, new Androlib()); + } - public ApkDecoder(File apkFile, Androlib androlib) { - mAndrolib = androlib; - setApkFile(apkFile); - } + public ApkDecoder(File apkFile, Androlib androlib) { + mAndrolib = androlib; + setApkFile(apkFile); + } - public void setApkFile(File apkFile) { - mApkFile = new ExtFile(apkFile); - mResTable = null; - } + public void setApkFile(File apkFile) { + mApkFile = new ExtFile(apkFile); + mResTable = null; + } - public void setOutDir(File outDir) throws AndrolibException { - mOutDir = outDir; - } + public void setOutDir(File outDir) throws AndrolibException { + mOutDir = outDir; + } - public void decode() throws AndrolibException, IOException { - File outDir = getOutDir(); + public void setApi(int api) { + mApi = api; + } - if (!mForceDelete && outDir.exists()) { - throw new OutDirExistsException(); - } + public void decode() throws AndrolibException, IOException { + File outDir = getOutDir(); - if (!mApkFile.isFile() || !mApkFile.canRead()) { - throw new InFileNotFoundException(); - } + if (!mForceDelete && outDir.exists()) { + throw new OutDirExistsException(); + } - try { - OS.rmdir(outDir); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - outDir.mkdirs(); + if (!mApkFile.isFile() || !mApkFile.canRead()) { + throw new InFileNotFoundException(); + } - if (hasSources()) { - switch (mDecodeSources) { - case DECODE_SOURCES_NONE: - mAndrolib.decodeSourcesRaw(mApkFile, outDir, mDebug); - break; - case DECODE_SOURCES_SMALI: - mAndrolib.decodeSourcesSmali(mApkFile, outDir, mDebug, mBakDeb); - break; - case DECODE_SOURCES_JAVA: - mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug); - break; - } - } + try { + OS.rmdir(outDir); + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + outDir.mkdirs(); - if (hasResources()) { + LOGGER.info("Using Apktool " + Androlib.getVersion() + " on " + mApkFile.getName()); - // read the resources.arsc checking for STORED vs DEFLATE - // compression - // this will determine whether we compress on rebuild or not. - JarFile jf = new JarFile(mApkFile.getAbsoluteFile()); - JarEntry je = jf.getJarEntry("resources.arsc"); - if (je != null) { - int compression = je.getMethod(); - mCompressResources = (compression != ZipEntry.STORED) - && (compression == ZipEntry.DEFLATED); - } - jf.close(); + if (hasResources()) { - switch (mDecodeResources) { - case DECODE_RESOURCES_NONE: - mAndrolib.decodeResourcesRaw(mApkFile, outDir); - break; - case DECODE_RESOURCES_FULL: - mAndrolib.decodeResourcesFull(mApkFile, outDir, getResTable()); - break; - } - } else { - // if there's no resources.asrc, decode the manifest without looking - // up attribute references - if (hasManifest()) { - switch (mDecodeResources) { - case DECODE_RESOURCES_NONE: - mAndrolib.decodeManifestRaw(mApkFile, outDir); - break; - case DECODE_RESOURCES_FULL: - mAndrolib.decodeManifestFull(mApkFile, outDir, - getResTable()); - break; - } - } - } + Map sdkInfo = mAndrolib.getResTable(mApkFile).getSdkInfo(); + if (sdkInfo.get("targetSdkVersion") != null) { + mApi = Integer.parseInt(sdkInfo.get("targetSdkVersion")); + } - mAndrolib.decodeRawFiles(mApkFile, outDir); - writeMetaFile(); - } + setAnalysisMode(mAnalysisMode, true); + // read the resources.arsc checking for STORED vs DEFLATE + // this will determine whether we compress on rebuild or not. + ZipExtFile zef = new ZipExtFile(mApkFile.getAbsolutePath()); + ZipArchiveEntry ze = zef.getEntry("resources.arsc"); + if (ze != null) { + int compression = ze.getMethod(); + mCompressResources = (compression == ZipEntry.DEFLATED); + } + zef.close(); - public void setDecodeSources(short mode) throws AndrolibException { - if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI - && mode != DECODE_SOURCES_JAVA) { - throw new AndrolibException("Invalid decode sources mode: " + mode); - } - mDecodeSources = mode; - } + switch (mDecodeResources) { + case DECODE_RESOURCES_NONE: + mAndrolib.decodeResourcesRaw(mApkFile, outDir); + break; + case DECODE_RESOURCES_FULL: + mAndrolib.decodeResourcesFull(mApkFile, outDir, getResTable()); + break; + } + } else { + // if there's no resources.asrc, decode the manifest without looking + // up attribute references + if (hasManifest()) { + switch (mDecodeResources) { + case DECODE_RESOURCES_NONE: + mAndrolib.decodeManifestRaw(mApkFile, outDir); + break; + case DECODE_RESOURCES_FULL: + mAndrolib.decodeManifestFull(mApkFile, outDir, + getResTable()); + break; + } + } + } - public void setDecodeResources(short mode) throws AndrolibException { - if (mode != DECODE_RESOURCES_NONE && mode != DECODE_RESOURCES_FULL) { - throw new AndrolibException("Invalid decode resources mode"); - } - mDecodeResources = mode; - } + if (hasSources()) { + switch (mDecodeSources) { + case DECODE_SOURCES_NONE: + mAndrolib.decodeSourcesRaw(mApkFile, outDir, mDebug); + break; + case DECODE_SOURCES_SMALI: + mAndrolib.decodeSourcesSmali(mApkFile, outDir, mDebug, mDebugLinePrefix, mBakDeb, mApi); + break; + case DECODE_SOURCES_JAVA: + mAndrolib.decodeSourcesJava(mApkFile, outDir, mDebug); + break; + } + } - public void setDebugMode(boolean debug) { - mDebug = debug; - } + mAndrolib.decodeRawFiles(mApkFile, outDir); + mAndrolib.decodeUnknownFiles(mApkFile, outDir, mResTable); + mAndrolib.writeOriginalFiles(mApkFile, outDir); + writeMetaFile(); + } - public void setBaksmaliDebugMode(boolean bakdeb) { - mBakDeb = bakdeb; - } + public void setDecodeSources(short mode) throws AndrolibException { + if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI + && mode != DECODE_SOURCES_JAVA) { + throw new AndrolibException("Invalid decode sources mode: " + mode); + } + mDecodeSources = mode; + } - public void setForceDelete(boolean forceDelete) { - mForceDelete = forceDelete; - } + public void setDecodeResources(short mode) throws AndrolibException { + if (mode != DECODE_RESOURCES_NONE && mode != DECODE_RESOURCES_FULL) { + throw new AndrolibException("Invalid decode resources mode"); + } + mDecodeResources = mode; + } - public void setFrameworkTag(String tag) throws AndrolibException { - mFrameTag = tag; - if (mResTable != null) { - getResTable().setFrameTag(tag); - } - } + public void setDebugMode(boolean debug) { + mDebug = debug; + } - public void setKeepBrokenResources(boolean keepBrokenResources) { - mKeepBrokenResources = keepBrokenResources; - } + public void setAnalysisMode(boolean mode, boolean pass) throws AndrolibException{ + mAnalysisMode = mode; - public void setFrameworkDir(String dir) { - mFrameworkDir = dir; - } + // only set mResTable, once it exists + if (pass) { + if (mResTable == null) { + mResTable = getResTable(); + } + mResTable.setAnalysisMode(mode); + } + } - public ResTable getResTable() throws AndrolibException { - if (mResTable == null) { - boolean hasResources = hasResources(); - boolean hasManifest = hasManifest(); - if (!(hasManifest || hasResources)) { - throw new AndrolibException( - "Apk doesn't contain either AndroidManifest.xml file or resources.arsc file"); - } - AndrolibResources.sKeepBroken = mKeepBrokenResources; - AndrolibResources.sFrameworkFolder = mFrameworkDir; - mResTable = mAndrolib.getResTable(mApkFile, hasResources); - mResTable.setFrameTag(mFrameTag); - } - return mResTable; - } + public void setDebugLinePrefix(String debugLinePrefix) { + mDebugLinePrefix = debugLinePrefix; + } - public boolean hasSources() throws AndrolibException { - try { - return mApkFile.getDirectory().containsFile("classes.dex"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public void setBaksmaliDebugMode(boolean bakdeb) { + mBakDeb = bakdeb; + } - public boolean hasManifest() throws AndrolibException { - try { - return mApkFile.getDirectory().containsFile("AndroidManifest.xml"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public void setForceDelete(boolean forceDelete) { + mForceDelete = forceDelete; + } - public boolean hasResources() throws AndrolibException { - try { - return mApkFile.getDirectory().containsFile("resources.arsc"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + public void setFrameworkTag(String tag) throws AndrolibException { + mFrameTag = tag; + if (mResTable != null) { + getResTable().setFrameTag(tag); + } + } - public final static short DECODE_SOURCES_NONE = 0x0000; - public final static short DECODE_SOURCES_SMALI = 0x0001; - public final static short DECODE_SOURCES_JAVA = 0x0002; + public void setKeepBrokenResources(boolean keepBrokenResources) { + mKeepBrokenResources = keepBrokenResources; + } - public final static short DECODE_RESOURCES_NONE = 0x0100; - public final static short DECODE_RESOURCES_FULL = 0x0101; + public void setFrameworkDir(String dir) { + mFrameworkDir = dir; + } - private File getOutDir() throws AndrolibException { - if (mOutDir == null) { - throw new AndrolibException("Out dir not set"); - } - return mOutDir; - } + public ResTable getResTable() throws AndrolibException { + if (mResTable == null) { + boolean hasResources = hasResources(); + boolean hasManifest = hasManifest(); + if (!(hasManifest || hasResources)) { + throw new AndrolibException( + "Apk doesn't contain either AndroidManifest.xml file or resources.arsc file"); + } + AndrolibResources.sKeepBroken = mKeepBrokenResources; + AndrolibResources.sFrameworkFolder = mFrameworkDir; + mResTable = mAndrolib.getResTable(mApkFile, hasResources); + mResTable.setFrameTag(mFrameTag); + } + return mResTable; + } - private void writeMetaFile() throws AndrolibException { - Map meta = new LinkedHashMap(); - meta.put("version", Androlib.getVersion()); - meta.put("apkFileName", mApkFile.getName()); + public boolean hasSources() throws AndrolibException { + try { + return mApkFile.getDirectory().containsFile("classes.dex"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - if (mDecodeResources != DECODE_RESOURCES_NONE - && (hasManifest() || hasResources())) { - meta.put("isFrameworkApk", - Boolean.valueOf(mAndrolib.isFrameworkApk(getResTable()))); - putUsesFramework(meta); - putSdkInfo(meta); - putPackageInfo(meta); - putCompressionInfo(meta); - } + public boolean hasManifest() throws AndrolibException { + try { + return mApkFile.getDirectory().containsFile("AndroidManifest.xml"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - mAndrolib.writeMetaFile(mOutDir, meta); - } + public boolean hasResources() throws AndrolibException { + try { + return mApkFile.getDirectory().containsFile("resources.arsc"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - private void putUsesFramework(Map meta) - throws AndrolibException { - Set pkgs = getResTable().listFramePackages(); - if (pkgs.isEmpty()) { - return; - } + public final static short DECODE_SOURCES_NONE = 0x0000; + public final static short DECODE_SOURCES_SMALI = 0x0001; + public final static short DECODE_SOURCES_JAVA = 0x0002; - Integer[] ids = new Integer[pkgs.size()]; - int i = 0; - for (ResPackage pkg : pkgs) { - ids[i++] = pkg.getId(); - } - Arrays.sort(ids); + public final static short DECODE_RESOURCES_NONE = 0x0100; + public final static short DECODE_RESOURCES_FULL = 0x0101; - Map uses = new LinkedHashMap(); - uses.put("ids", ids); + private File getOutDir() throws AndrolibException { + if (mOutDir == null) { + throw new AndrolibException("Out dir not set"); + } + return mOutDir; + } - if (mFrameTag != null) { - uses.put("tag", mFrameTag); - } + private void writeMetaFile() throws AndrolibException { + Map meta = new LinkedHashMap(); + meta.put("version", Androlib.getVersion()); + meta.put("apkFileName", mApkFile.getName()); - meta.put("usesFramework", uses); - } + if (mDecodeResources != DECODE_RESOURCES_NONE + && (hasManifest() || hasResources())) { + meta.put("isFrameworkApk", + Boolean.valueOf(mAndrolib.isFrameworkApk(getResTable()))); + putUsesFramework(meta); + putSdkInfo(meta); + putPackageInfo(meta); + putVersionInfo(meta); + putCompressionInfo(meta); + } + putUnknownInfo(meta); - private void putSdkInfo(Map meta) throws AndrolibException { - Map info = getResTable().getSdkInfo(); - if (info.size() > 0) { - meta.put("sdkInfo", info); - } - } + mAndrolib.writeMetaFile(mOutDir, meta); + } - private void putPackageInfo(Map meta) - throws AndrolibException { - Map info = getResTable().getPackageInfo(); - if (info.size() > 0) { - meta.put("packageInfo", info); - } - } + private void putUsesFramework(Map meta) + throws AndrolibException { + Set pkgs = getResTable().listFramePackages(); + if (pkgs.isEmpty()) { + return; + } - private void putCompressionInfo(Map meta) - throws AndrolibException { - meta.put("compressionType", getCompressionType()); - } + Integer[] ids = new Integer[pkgs.size()]; + int i = 0; + for (ResPackage pkg : pkgs) { + ids[i++] = pkg.getId(); + } + Arrays.sort(ids); - private boolean getCompressionType() { - return mCompressResources; - } + Map uses = new LinkedHashMap(); + uses.put("ids", ids); - private final Androlib mAndrolib; + if (mFrameTag != null) { + uses.put("tag", mFrameTag); + } - private ExtFile mApkFile; - private File mOutDir; - private ResTable mResTable; - private short mDecodeSources = DECODE_SOURCES_SMALI; - private short mDecodeResources = DECODE_RESOURCES_FULL; - private boolean mDebug = false; - private boolean mForceDelete = false; - private String mFrameTag; - private boolean mKeepBrokenResources = false; - private String mFrameworkDir = null; - private boolean mBakDeb = true; - private boolean mCompressResources = false; + meta.put("usesFramework", uses); + } + + private void putSdkInfo(Map meta) + throws AndrolibException { + Map info = getResTable().getSdkInfo(); + if (info.size() > 0) { + meta.put("sdkInfo", info); + } + } + + private void putPackageInfo(Map meta) + throws AndrolibException { + String renamed = getResTable().getPackageRenamed(); + String original = getResTable().getPackageOriginal(); + int id = getResTable().getPackageId(); + + HashMap packages = new HashMap(); + + // only put rename-manifest-package into apktool.yml, if the change will be required + if (!renamed.equalsIgnoreCase(original)) { + packages.put("rename-manifest-package", renamed); + } + packages.put("forced-package-id", String.valueOf(id)); + meta.put("packageInfo", packages); + } + + private void putVersionInfo(Map meta) + throws AndrolibException { + Map info = getResTable().getVersionInfo(); + if (info.size() > 0) { + meta.put("versionInfo", info); + } + } + + private void putUnknownInfo(Map meta) + throws AndrolibException { + Map info = mAndrolib.mResUnknownFiles.getUnknownFiles(); + if (info.size() > 0) { + meta.put("unknownFiles", info); + } + } + + private void putCompressionInfo(Map meta) + throws AndrolibException { + meta.put("compressionType", getCompressionType()); + } + + private boolean getCompressionType() { + return mCompressResources; + } + + private final Androlib mAndrolib; + + private final static Logger LOGGER = Logger.getLogger(Androlib.class.getName()); + + private ExtFile mApkFile; + private File mOutDir; + private ResTable mResTable; + private short mDecodeSources = DECODE_SOURCES_SMALI; + private short mDecodeResources = DECODE_RESOURCES_FULL; + private boolean mDebug = false; + private String mDebugLinePrefix = "a=0;// "; + private boolean mForceDelete = false; + private String mFrameTag; + private boolean mKeepBrokenResources = false; + private String mFrameworkDir = null; + private int mApi = 15; + private boolean mBakDeb = true; + private boolean mCompressResources = false; + private boolean mAnalysisMode = false; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java index 01be7808..aa1f0d76 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java @@ -16,6 +16,9 @@ package brut.androlib; +import org.jf.baksmali.baksmali; +import org.jf.smali.main; + import java.io.IOException; import java.io.InputStream; import java.util.Properties; @@ -25,52 +28,56 @@ import java.util.logging.Logger; * @author Ryszard Wiśniewski */ public class ApktoolProperties { - public static String get(String key) { - return get().getProperty(key); - } + public static String get(String key) { + return get().getProperty(key); + } - public static Properties get() { - if (sProps == null) { - loadProps(); - } - return sProps; - } + public static Properties get() { + if (sProps == null) { + loadProps(); + } + return sProps; + } - private static void loadProps() { - InputStream in = ApktoolProperties.class - .getResourceAsStream("/properties/apktool.properties"); - sProps = new Properties(); - try { - sProps.load(in); - in.close(); - } catch (IOException ex) { - LOGGER.warning("Can't load properties."); - } + private static void loadProps() { + InputStream in = ApktoolProperties.class.getResourceAsStream("/properties/apktool.properties"); + sProps = new Properties(); + try { + sProps.load(in); + in.close(); + } catch (IOException ex) { + LOGGER.warning("Can't load properties."); + } - InputStream templateStream = ApktoolProperties.class - .getResourceAsStream("/properties/baksmali.properties"); - Properties properties = new Properties(); - String version = "(unknown)"; - try { - properties.load(templateStream); - version = properties.getProperty("application.version"); - } catch (IOException ex) { - } - sProps.put("baksmaliVersion", version); - templateStream = ApktoolProperties.class - .getResourceAsStream("/properties/smali.properties"); - properties = new Properties(); - version = "(unknown)"; - try { - properties.load(templateStream); - version = properties.getProperty("application.version"); - } catch (IOException ex) { - } - sProps.put("smaliVersion", version); - } + InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties"); + Properties properties = new Properties(); + String version = "(unknown)"; - private static Properties sProps; + if (templateStream != null) { + try { + properties.load(templateStream); + version = properties.getProperty("application.version"); + } catch (IOException ignored) { + } + } + sProps.put("baksmaliVersion", version); - private static final Logger LOGGER = Logger - .getLogger(ApktoolProperties.class.getName()); + templateStream = main.class.getClassLoader().getResourceAsStream("smali.properties"); + properties = new Properties(); + version = "(unknown)"; + + if (templateStream != null) { + try { + properties.load(templateStream); + version = properties.getProperty("application.version"); + } catch (IOException ignored) { + } + } + sProps.put("smaliVersion", version); + } + + private static Properties sProps; + + private static final Logger LOGGER = Logger + .getLogger(ApktoolProperties.class.getName()); } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java index c65faac1..7bfc4e5b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java @@ -20,7 +20,8 @@ import java.io.*; import org.antlr.runtime.*; import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTreeNodeStream; -import org.jf.dexlib.DexFile; +import org.apache.commons.io.IOUtils; +import org.jf.dexlib2.writer.builder.DexBuilder; import org.jf.smali.*; /** @@ -28,58 +29,73 @@ import org.jf.smali.*; */ public class SmaliMod { - public static boolean assembleSmaliFile(InputStream smaliStream, - String name, DexFile dexFile, boolean verboseErrors, - boolean oldLexer, boolean printTokens) throws IOException, - RecognitionException { - CommonTokenStream tokens; + public static boolean assembleSmaliFile(String smali, DexBuilder dexBuilder, boolean verboseErrors, + boolean printTokens, File smaliFile) throws IOException, RuntimeException, RecognitionException { - boolean lexerErrors = false; - LexerErrorInterface lexer; + InputStream is = new ByteArrayInputStream(smali.getBytes()); + return assembleSmaliFile(is, dexBuilder, verboseErrors, printTokens, smaliFile); + } - InputStreamReader reader = new InputStreamReader(smaliStream, "UTF-8"); + public static boolean assembleSmaliFile(InputStream is,DexBuilder dexBuilder, boolean verboseErrors, + boolean printTokens, File smaliFile) throws IOException, RecognitionException { - lexer = new smaliFlexLexer(reader); - tokens = new CommonTokenStream((TokenSource) lexer); + // copy our filestream into a tmp file, so we don't overwrite + File tmp = File.createTempFile("BRUT",".bak"); + tmp.deleteOnExit(); - if (printTokens) { - tokens.getTokens(); + OutputStream os = new FileOutputStream(tmp); + IOUtils.copy(is, os); + os.close(); - for (int i = 0; i < tokens.size(); i++) { - Token token = tokens.get(i); - if (token.getChannel() == BaseRecognizer.HIDDEN) { - continue; - } + return assembleSmaliFile(tmp,dexBuilder, verboseErrors, printTokens); + } - System.out.println(smaliParser.tokenNames[token.getType()] - + ": " + token.getText()); - } - } + public static boolean assembleSmaliFile(File smaliFile,DexBuilder dexBuilder, boolean verboseErrors, + boolean printTokens) throws IOException, RecognitionException { - smaliParser parser = new smaliParser(tokens); - parser.setVerboseErrors(verboseErrors); + CommonTokenStream tokens; + LexerErrorInterface lexer; - smaliParser.smali_file_return result = parser.smali_file(); + InputStream is = new FileInputStream(smaliFile); + InputStreamReader reader = new InputStreamReader(is, "UTF-8"); - if (parser.getNumberOfSyntaxErrors() > 0 - || lexer.getNumberOfSyntaxErrors() > 0) { - return false; - } + lexer = new smaliFlexLexer(reader); + ((smaliFlexLexer)lexer).setSourceFile(smaliFile); + tokens = new CommonTokenStream((TokenSource) lexer); - CommonTree t = (CommonTree) result.getTree(); + if (printTokens) { + tokens.getTokens(); - CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); - treeStream.setTokenStream(tokens); + for (int i=0; i 0) { - return false; - } + smaliParser.smali_file_return result = parser.smali_file(); - return true; - } + if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) { + return false; + } + + CommonTree t = (CommonTree) result.getTree(); + + CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); + treeStream.setTokenStream(tokens); + + smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); + + dexGen.setVerboseErrors(verboseErrors); + dexGen.setDexBuilder(dexBuilder); + dexGen.smali_file(); + + return dexGen.getNumberOfSyntaxErrors() == 0; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java index c5b4627e..6ede4d48 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java @@ -37,17 +37,15 @@ import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; +import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import org.apache.commons.io.IOUtils; import org.xml.sax.SAXException; @@ -57,695 +55,808 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ final public class AndrolibResources { - public ResTable getResTable(ExtFile apkFile) throws AndrolibException { - return getResTable(apkFile, true); - } - - public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) - throws AndrolibException { - ResTable resTable = new ResTable(this); - if (loadMainPkg) { - loadMainPkg(resTable, apkFile); - } - return resTable; - } - - public ResPackage loadMainPkg(ResTable resTable, ExtFile apkFile) - throws AndrolibException { - LOGGER.info("Loading resource table..."); - ResPackage[] pkgs = getResPackagesFromApk(apkFile, resTable, - sKeepBroken); - ResPackage pkg = null; - - switch (pkgs.length) { - case 1: - pkg = pkgs[0]; - break; - case 2: - if (pkgs[0].getName().equals("android")) { - LOGGER.warning("Skipping \"android\" package group"); - pkg = pkgs[1]; - } else if (pkgs[0].getName().equals("com.htc")) { - LOGGER.warning("Skipping \"htc\" package group"); - pkg = pkgs[1]; - } - break; - } - - if (pkg == null) { - throw new AndrolibException( - "Arsc files with zero or multiple packages"); - } - - resTable.addPackage(pkg, true); - LOGGER.info("Loaded."); - return pkg; - } - - public ResPackage loadFrameworkPkg(ResTable resTable, int id, - String frameTag) throws AndrolibException { - File apk = getFrameworkApk(id, frameTag); - - LOGGER.info("Loading resource table from file: " + apk); - ResPackage[] pkgs = getResPackagesFromApk(new ExtFile(apk), resTable, - true); - - if (pkgs.length != 1) { - throw new AndrolibException( - "Arsc files with zero or multiple packages"); - } - - ResPackage pkg = pkgs[0]; - if (pkg.getId() != id) { - throw new AndrolibException("Expected pkg of id: " - + String.valueOf(id) + ", got: " + pkg.getId()); - } - - resTable.addPackage(pkg, false); - LOGGER.info("Loaded."); - return pkg; - } - - public void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir) - throws AndrolibException { - - Duo duo = getManifestFileDecoder(); - ResFileDecoder fileDecoder = duo.m1; - - // Set ResAttrDecoder - duo.m2.setAttrDecoder(new ResAttrDecoder()); - ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); - - // Fake ResPackage - attrDecoder.setCurrentPackage(new ResPackage(resTable, 0, null)); - - Directory inApk, out; - try { - inApk = apkFile.getDirectory(); - out = new FileDirectory(outDir); - - LOGGER.info("Decoding AndroidManifest.xml with only framework resources..."); - fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, - "AndroidManifest.xml"); - - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } - - public void remove_application_debug(String filePath) - throws AndrolibException { - - // change application:debug to true - try { - DocumentBuilderFactory docFactory = DocumentBuilderFactory - .newInstance(); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - Document doc = docBuilder.parse(filePath.toString()); - - Node application = doc.getElementById("application"); - - // load attr - NamedNodeMap attr = application.getAttributes(); - Node debugAttr = attr.getNamedItem("debug"); - - // remove application:debug - if (debugAttr != null) { - attr.removeNamedItem("debug"); - } - - // save manifest - TransformerFactory transformerFactory = TransformerFactory - .newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); - StreamResult result = new StreamResult(new File(filePath)); - transformer.transform(source, result); - - } catch (ParserConfigurationException ex) { - throw new AndrolibException(ex); - } catch (SAXException ex) { - throw new AndrolibException(ex); - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (TransformerConfigurationException ex) { - throw new AndrolibException(ex); - } catch (TransformerException ex) { - throw new AndrolibException(ex); - } - } - - public void adjust_package_manifest(ResTable resTable, String filePath) - throws AndrolibException { - - // check if packages different, and that package is not equal to - // "android" - Map packageInfo = resTable.getPackageInfo(); - if ((packageInfo.get("cur_package").equalsIgnoreCase( - packageInfo.get("orig_package")) || ("android" - .equalsIgnoreCase(packageInfo.get("cur_package")) || ("com.htc" - .equalsIgnoreCase(packageInfo.get("cur_package")))))) { - - LOGGER.info("Regular manifest package..."); - } else { - try { - - LOGGER.info("Renamed manifest package found! Fixing..."); - DocumentBuilderFactory docFactory = DocumentBuilderFactory - .newInstance(); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - Document doc = docBuilder.parse(filePath.toString()); - - // Get the manifest line - Node manifest = doc.getFirstChild(); - - // update package attribute - NamedNodeMap attr = manifest.getAttributes(); - Node nodeAttr = attr.getNamedItem("package"); - mPackageRenamed = nodeAttr.getNodeValue(); - nodeAttr.setNodeValue(packageInfo.get("cur_package")); - - // re-save manifest. - TransformerFactory transformerFactory = TransformerFactory - .newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); - StreamResult result = new StreamResult(new File(filePath)); - transformer.transform(source, result); - - } catch (ParserConfigurationException ex) { - throw new AndrolibException(ex); - } catch (TransformerException ex) { - throw new AndrolibException(ex); - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (SAXException ex) { - throw new AndrolibException(ex); - } - } - } - - public void decode(ResTable resTable, ExtFile apkFile, File outDir) - throws AndrolibException { - Duo duo = getResFileDecoder(); - ResFileDecoder fileDecoder = duo.m1; - ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); - - attrDecoder.setCurrentPackage(resTable.listMainPackages().iterator() - .next()); - - Directory inApk, in = null, out; - try { - inApk = apkFile.getDirectory(); - out = new FileDirectory(outDir); - - LOGGER.info("Decoding AndroidManifest.xml with resources..."); - - fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, - "AndroidManifest.xml"); - - // fix package if needed - adjust_package_manifest(resTable, outDir.getAbsolutePath() - + "/AndroidManifest.xml"); - - if (inApk.containsDir("res")) { - in = inApk.getDir("res"); - } - out = out.createDir("res"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - - ExtMXSerializer xmlSerializer = getResXmlSerializer(); - for (ResPackage pkg : resTable.listMainPackages()) { - attrDecoder.setCurrentPackage(pkg); - - LOGGER.info("Decoding file-resources..."); - for (ResResource res : pkg.listFiles()) { - fileDecoder.decode(res, in, out); - } - - LOGGER.info("Decoding values */* XMLs..."); - for (ResValuesFile valuesFile : pkg.listValuesFiles()) { - generateValuesFile(valuesFile, out, xmlSerializer); - } - generatePublicXml(pkg, out, xmlSerializer); - LOGGER.info("Done."); - } - - AndrolibException decodeError = duo.m2.getFirstError(); - if (decodeError != null) { - throw decodeError; - } - } - - public void setSdkInfo(Map map) { - if (map != null) { - mMinSdkVersion = map.get("minSdkVersion"); - mTargetSdkVersion = map.get("targetSdkVersion"); - mMaxSdkVersion = map.get("maxSdkVersion"); - } - } - - public void setPackageInfo(Map map) { - if (map != null) { - mPackageRenamed = map.get("package"); - } - } - - public void aaptPackage(File apkFile, File manifest, File resDir, - File rawDir, File assetDir, File[] include, - HashMap flags, String aaptPath) - throws AndrolibException { - - List cmd = new ArrayList(); - - // path for aapt binary - if (!aaptPath.isEmpty()) { - File aaptFile = new File(aaptPath); - if (aaptFile.canRead() && aaptFile.exists()) { - aaptFile.setExecutable(true); - cmd.add(aaptFile.getPath()); - - if (flags.get("verbose")) { - LOGGER.info(aaptFile.getPath() - + " being used as aapt location."); - } - } else { - LOGGER.warning("aapt location could not be found. Defaulting back to default"); - cmd.add("aapt"); - } - } else { - cmd.add("aapt"); - } - - cmd.add("p"); - - if (flags.get("verbose")) { // output aapt verbose - cmd.add("-v"); - } - if (flags.get("update")) { - cmd.add("-u"); - } - if (flags.get("debug")) { // inject debuggable="true" into manifest - cmd.add("--debug-mode"); - } - if (mMinSdkVersion != null) { - cmd.add("--min-sdk-version"); - cmd.add(mMinSdkVersion); - } - if (mTargetSdkVersion != null) { - cmd.add("--target-sdk-version"); - cmd.add(mTargetSdkVersion); - } - if (mMaxSdkVersion != null) { - cmd.add("--max-sdk-version"); - cmd.add(mMaxSdkVersion); - } - if (mPackageRenamed != null) { - cmd.add("--rename-manifest-package"); - cmd.add(mPackageRenamed); - } - cmd.add("-F"); - cmd.add(apkFile.getAbsolutePath()); - - if (flags.get("framework")) { - cmd.add("-x"); - } - - if (!(flags.get("compression"))) { - cmd.add("-0"); - cmd.add("arsc"); - } - - if (include != null) { - for (File file : include) { - cmd.add("-I"); - cmd.add(file.getPath()); - } - } - if (resDir != null) { - cmd.add("-S"); - cmd.add(resDir.getAbsolutePath()); - } - if (manifest != null) { - cmd.add("-M"); - cmd.add(manifest.getAbsolutePath()); - } - if (assetDir != null) { - cmd.add("-A"); - cmd.add(assetDir.getAbsolutePath()); - } - if (rawDir != null) { - cmd.add(rawDir.getAbsolutePath()); - } - - try { - OS.exec(cmd.toArray(new String[0])); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - - public boolean detectWhetherAppIsFramework(File appDir) - throws AndrolibException { - File publicXml = new File(appDir, "res/values/public.xml"); - if (!publicXml.exists()) { - return false; - } - - Iterator it; - try { - it = IOUtils.lineIterator(new FileReader(new File(appDir, - "res/values/public.xml"))); - } catch (FileNotFoundException ex) { - throw new AndrolibException( - "Could not detect whether app is framework one", ex); - } - it.next(); - it.next(); - return it.next().contains("0x01"); - } - - public void tagSmaliResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - new ResSmaliUpdater().tagResIDs(resTable, smaliDir); - } - - public void updateSmaliResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - new ResSmaliUpdater().updateResIDs(resTable, smaliDir); - } - - public Duo getResFileDecoder() { - ResStreamDecoderContainer decoders = new ResStreamDecoderContainer(); - decoders.setDecoder("raw", new ResRawStreamDecoder()); - decoders.setDecoder("9patch", new Res9patchStreamDecoder()); - - AXmlResourceParser axmlParser = new AXmlResourceParser(); - axmlParser.setAttrDecoder(new ResAttrDecoder()); - decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser, - getResXmlSerializer())); - - return new Duo(new ResFileDecoder( - decoders), axmlParser); - } - - public Duo getManifestFileDecoder() { - ResStreamDecoderContainer decoders = new ResStreamDecoderContainer(); - - AXmlResourceParser axmlParser = new AXmlResourceParser(); - - decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser, - getResXmlSerializer())); - - return new Duo(new ResFileDecoder( - decoders), axmlParser); - } - - public ExtMXSerializer getResXmlSerializer() { - ExtMXSerializer serial = new ExtMXSerializer(); - serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION, - " "); - serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, - System.getProperty("line.separator")); - serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8"); - serial.setDisabledAttrEscape(true); - return serial; - } - - private void generateValuesFile(ResValuesFile valuesFile, Directory out, - ExtXmlSerializer serial) throws AndrolibException { - try { - OutputStream outStream = out.getFileOutput(valuesFile.getPath()); - serial.setOutput((outStream), null); - serial.startDocument(null, null); - serial.startTag(null, "resources"); - - for (ResResource res : valuesFile.listResources()) { - if (valuesFile.isSynthesized(res)) { - continue; - } - ((ResValuesXmlSerializable) res.getValue()) - .serializeToResValuesXml(serial, res); - } - - serial.endTag(null, "resources"); - serial.newLine(); - serial.endDocument(); - serial.flush(); - outStream.close(); - } catch (IOException ex) { - throw new AndrolibException("Could not generate: " - + valuesFile.getPath(), ex); - } catch (DirectoryException ex) { - throw new AndrolibException("Could not generate: " - + valuesFile.getPath(), ex); - } - } - - private void generatePublicXml(ResPackage pkg, Directory out, - XmlSerializer serial) throws AndrolibException { - try { - OutputStream outStream = out.getFileOutput("values/public.xml"); - serial.setOutput(outStream, null); - serial.startDocument(null, null); - serial.startTag(null, "resources"); - - for (ResResSpec spec : pkg.listResSpecs()) { - serial.startTag(null, "public"); - serial.attribute(null, "type", spec.getType().getName()); - serial.attribute(null, "name", spec.getName()); - serial.attribute(null, "id", - String.format("0x%08x", spec.getId().id)); - serial.endTag(null, "public"); - } - - serial.endTag(null, "resources"); - serial.endDocument(); - serial.flush(); - outStream.close(); - } catch (IOException ex) { - throw new AndrolibException("Could not generate public.xml file", - ex); - } catch (DirectoryException ex) { - throw new AndrolibException("Could not generate public.xml file", - ex); - } - } - - private ResPackage[] getResPackagesFromApk(ExtFile apkFile, - ResTable resTable, boolean keepBroken) throws AndrolibException { - try { - return ARSCDecoder.decode( - apkFile.getDirectory().getFileInput("resources.arsc"), - false, keepBroken, resTable).getPackages(); - } catch (DirectoryException ex) { - throw new AndrolibException( - "Could not load resources.arsc from file: " + apkFile, ex); - } - } - - public File getFrameworkApk(int id, String frameTag) - throws AndrolibException { - File dir = getFrameworkDir(); - File apk; - - if (frameTag != null) { - apk = new File(dir, String.valueOf(id) + '-' + frameTag + ".apk"); - if (apk.exists()) { - return apk; - } - } - - apk = new File(dir, String.valueOf(id) + ".apk"); - if (apk.exists()) { - return apk; - } - - if (id == 1) { - InputStream in = null; - OutputStream out = null; - try { - in = AndrolibResources.class - .getResourceAsStream("/brut/androlib/android-framework.jar"); - out = new FileOutputStream(apk); - IOUtils.copy(in, out); - return apk; - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) { - } - } - if (out != null) { - try { - out.close(); - } catch (IOException ex) { - } - } - } - } - - throw new CantFindFrameworkResException(id); - } - - public void installFramework(File frameFile, String tag) - throws AndrolibException { - InputStream in = null; - ZipOutputStream out = null; - try { - ZipFile zip = new ZipFile(frameFile); - ZipEntry entry = zip.getEntry("resources.arsc"); - - if (entry == null) { - throw new AndrolibException("Can't find resources.arsc file"); - } - - in = zip.getInputStream(entry); - byte[] data = IOUtils.toByteArray(in); - - ARSCData arsc = ARSCDecoder.decode(new ByteArrayInputStream(data), - true, true); - publicizeResources(data, arsc.getFlagsOffsets()); - - File outFile = new File(getFrameworkDir(), String.valueOf(arsc - .getOnePackage().getId()) - + (tag == null ? "" : '-' + tag) - + ".apk"); - - out = new ZipOutputStream(new FileOutputStream(outFile)); - out.setMethod(ZipOutputStream.STORED); - CRC32 crc = new CRC32(); - crc.update(data); - entry = new ZipEntry("resources.arsc"); - entry.setSize(data.length); - entry.setCrc(crc.getValue()); - out.putNextEntry(entry); - out.write(data); - LOGGER.info("Framework installed to: " + outFile); - } catch (ZipException ex) { - throw new AndrolibException(ex); - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) { - } - } - if (out != null) { - try { - out.close(); - } catch (IOException ex) { - } - } - } - } - - public void publicizeResources(File arscFile) throws AndrolibException { - byte[] data = new byte[(int) arscFile.length()]; - - InputStream in = null; - OutputStream out = null; - try { - in = new FileInputStream(arscFile); - in.read(data); - - publicizeResources(data); - - out = new FileOutputStream(arscFile); - out.write(data); - } catch (IOException ex) { - throw new AndrolibException(ex); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ex) { - } - } - if (out != null) { - try { - out.close(); - } catch (IOException ex) { - } - } - } - } - - public void publicizeResources(byte[] arsc) throws AndrolibException { - publicizeResources(arsc, - ARSCDecoder.decode(new ByteArrayInputStream(arsc), true, true) - .getFlagsOffsets()); - } - - public void publicizeResources(byte[] arsc, FlagsOffset[] flagsOffsets) - throws AndrolibException { - for (FlagsOffset flags : flagsOffsets) { - int offset = flags.offset + 3; - int end = offset + 4 * flags.count; - while (offset < end) { - arsc[offset] |= (byte) 0x40; - offset += 4; - } - } - } - - private File getFrameworkDir() throws AndrolibException { - String path; - - // if a framework path was specified on the command line, use it - if (sFrameworkFolder != null) { - path = sFrameworkFolder; - } else if (System.getProperty("os.name").equals("Mac OS X")) { - // store in user-home, for Mac OS X - path = System.getProperty("user.home") + File.separatorChar + "Library/apktool/framework"; - } else { - path = System.getProperty("user.home") + File.separatorChar + "apktool" + File.separatorChar + "framework"; - } - - File dir = new File(path); - if (!dir.exists()) { - if (!dir.mkdirs()) { - if (sFrameworkFolder != null) { - System.out.println("Can't create Framework directory: " - + dir); - } - throw new AndrolibException("Can't create directory: " + dir); - } - } - return dir; - } - - public File getAndroidResourcesFile() throws AndrolibException { - try { - return Jar - .getResourceAsFile("/brut/androlib/android-framework.jar"); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } - } - - public void setFrameworkFolder(String path) { - sFrameworkFolder = path; - } - - // TODO: dirty static hack. I have to refactor decoding mechanisms. - public static boolean sKeepBroken = false; - public static String sFrameworkFolder = null; - - private final static Logger LOGGER = Logger - .getLogger(AndrolibResources.class.getName()); - - private String mMinSdkVersion = null; - private String mMaxSdkVersion = null; - private String mTargetSdkVersion = null; - - private String mPackageRenamed = null; - + public ResTable getResTable(ExtFile apkFile) throws AndrolibException { + return getResTable(apkFile, true); + } + + public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) + throws AndrolibException { + ResTable resTable = new ResTable(this); + if (loadMainPkg) { + loadMainPkg(resTable, apkFile); + } + return resTable; + } + + public ResPackage loadMainPkg(ResTable resTable, ExtFile apkFile) + throws AndrolibException { + LOGGER.info("Loading resource table..."); + ResPackage[] pkgs = getResPackagesFromApk(apkFile, resTable, + sKeepBroken); + ResPackage pkg = null; + + // @todo handle multiple packages using findPackageWithMostResSpecs() + switch (pkgs.length) { + case 1: + pkg = pkgs[0]; + break; + case 2: + if (pkgs[0].getName().equals("android")) { + LOGGER.warning("Skipping \"android\" package group"); + pkg = pkgs[1]; + } else if (pkgs[0].getName().equals("com.htc")) { + LOGGER.warning("Skipping \"htc\" package group"); + pkg = pkgs[1]; + } + break; + } + + if (pkg == null) { + throw new AndrolibException( + "Arsc files with zero or multiple packages"); + } + + resTable.addPackage(pkg, true); + return pkg; + } + + public ResPackage loadFrameworkPkg(ResTable resTable, int id, + String frameTag) throws AndrolibException { + File apk = getFrameworkApk(id, frameTag); + + LOGGER.info("Loading resource table from file: " + apk); + ResPackage[] pkgs = getResPackagesFromApk(new ExtFile(apk), resTable, + true); + + if (pkgs.length != 1) { + throw new AndrolibException( + "Arsc files with zero or multiple packages"); + } + + ResPackage pkg = pkgs[0]; + if (pkg.getId() != id) { + throw new AndrolibException("Expected pkg of id: " + + String.valueOf(id) + ", got: " + pkg.getId()); + } + + resTable.addPackage(pkg, false); + return pkg; + } + + public void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir) + throws AndrolibException { + + Duo duo = getManifestFileDecoder(); + ResFileDecoder fileDecoder = duo.m1; + + // Set ResAttrDecoder + duo.m2.setAttrDecoder(new ResAttrDecoder()); + ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); + + // Fake ResPackage + attrDecoder.setCurrentPackage(new ResPackage(resTable, 0, null)); + + Directory inApk, out; + try { + inApk = apkFile.getDirectory(); + out = new FileDirectory(outDir); + + LOGGER.info("Decoding AndroidManifest.xml with only framework resources..."); + fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, + "AndroidManifest.xml"); + + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + + public void remove_application_debug(String filePath) + throws AndrolibException { + + // change application:debug to true + try { + Document doc = loadDocument(filePath); + Node application = doc.getElementById("application"); + + // load attr + NamedNodeMap attr = application.getAttributes(); + Node debugAttr = attr.getNamedItem("debug"); + + // remove application:debug + if (debugAttr != null) { + attr.removeNamedItem("debug"); + } + + saveDocument(filePath, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + } + } + + public void adjust_package_manifest(ResTable resTable, String filePath) + throws AndrolibException { + + // compare resources.arsc package name to the one present in AndroidManifest + ResPackage resPackage = resTable.getCurrentResPackage(); + mPackageOriginal = resPackage.getName(); + mPackageRenamed = resTable.getPackageRenamed(); + + resTable.setPackageId(resPackage.getId()); + resTable.setPackageOriginal(mPackageOriginal); + + // 1) Check if mPackageOriginal === mPackageRenamed + // 2) Check if mPackageOriginal is ignored via IGNORED_PACKAGES + // 2a) If its ignored, make sure the mPackageRenamed isn't explicitly allowed + if (mPackageOriginal.equalsIgnoreCase(mPackageRenamed) || + (Arrays.asList(IGNORED_PACKAGES).contains(mPackageOriginal) && + ! Arrays.asList(ALLOWED_PACKAGES).contains(mPackageRenamed))) { + LOGGER.info("Regular manifest package..."); + } else { + try { + LOGGER.info("Renamed manifest package found! Replacing " + mPackageRenamed + " with " + mPackageOriginal); + Document doc = loadDocument(filePath); + + // Get the manifest line + Node manifest = doc.getFirstChild(); + + // update package attribute + NamedNodeMap attr = manifest.getAttributes(); + Node nodeAttr = attr.getNamedItem("package"); + nodeAttr.setNodeValue(mPackageOriginal); + saveDocument(filePath, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + } + } + } + + public void remove_manifest_versions(String filePath) + throws AndrolibException { + + File f = new File(filePath); + + if (f.exists()) { + try { + Document doc = loadDocument(filePath); + Node manifest = doc.getFirstChild(); + + // load attr + NamedNodeMap attr = manifest.getAttributes(); + Node vCode = attr.getNamedItem("android:versionCode"); + Node vName = attr.getNamedItem("android:versionName"); + + // remove versionCode + if (vCode != null) { + attr.removeNamedItem("android:versionCode"); + } + if (vName != null) { + attr.removeNamedItem("android:versionName"); + } + saveDocument(filePath, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + } + } + } + + private Document loadDocument(String filename) + throws IOException, SAXException, ParserConfigurationException { + + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + Document doc = docBuilder.parse(filename); + return doc; + } + + private void saveDocument(String filename, Document doc) + throws IOException, SAXException, ParserConfigurationException, TransformerException { + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.STANDALONE,"yes"); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(new File(filename)); + transformer.transform(source, result); + } + + public void decode(ResTable resTable, ExtFile apkFile, File outDir) + throws AndrolibException { + Duo duo = getResFileDecoder(); + ResFileDecoder fileDecoder = duo.m1; + ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); + + attrDecoder.setCurrentPackage(resTable.listMainPackages().iterator() + .next()); + + Directory inApk, in = null, out; + try { + inApk = apkFile.getDirectory(); + out = new FileDirectory(outDir); + + LOGGER.info("Decoding AndroidManifest.xml with resources..."); + + fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, + "AndroidManifest.xml"); + + // Remove versionName / versionCode (aapt API 16) + if (!resTable.getAnalysisMode()) { + + // check for a mismatch between resources.arsc package and the package listed in AndroidManifest + // also remove the android::versionCode / versionName from manifest for rebuild + // this is a required change to prevent aapt warning about conflicting versions + // it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml + adjust_package_manifest(resTable, outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"); + remove_manifest_versions(outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"); + mPackageId = String.valueOf(resTable.getPackageId()); + } + if (inApk.containsDir("res")) { + in = inApk.getDir("res"); + } + out = out.createDir("res"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + + ExtMXSerializer xmlSerializer = getResXmlSerializer(); + for (ResPackage pkg : resTable.listMainPackages()) { + attrDecoder.setCurrentPackage(pkg); + + LOGGER.info("Decoding file-resources..."); + for (ResResource res : pkg.listFiles()) { + fileDecoder.decode(res, in, out); + } + + LOGGER.info("Decoding values */* XMLs..."); + for (ResValuesFile valuesFile : pkg.listValuesFiles()) { + generateValuesFile(valuesFile, out, xmlSerializer); + } + generatePublicXml(pkg, out, xmlSerializer); + } + + AndrolibException decodeError = duo.m2.getFirstError(); + if (decodeError != null) { + throw decodeError; + } + } + + public void setSdkInfo(Map map) { + if (map != null) { + mMinSdkVersion = map.get("minSdkVersion"); + mTargetSdkVersion = map.get("targetSdkVersion"); + mMaxSdkVersion = map.get("maxSdkVersion"); + } + } + + public void setVersionInfo(Map map) { + if (map != null) { + mVersionCode = map.get("versionCode"); + mVersionName = map.get("versionName"); + } + } + + public void setPackageInfo(Map map) { + if (map != null) { + mPackageRenamed = map.get("rename-manifest-package"); + } + } + + public void setPackageId(Map map) { + if (map != null) { + mPackageId = map.get("forced-package-id"); + } + } + + public void aaptPackage(File apkFile, File manifest, File resDir, + File rawDir, File assetDir, File[] include, + HashMap flags, String aaptPath) + throws AndrolibException { + + boolean customAapt = false; + List cmd = new ArrayList(); + + // path for aapt binary + if (!aaptPath.isEmpty()) { + File aaptFile = new File(aaptPath); + if (aaptFile.canRead() && aaptFile.exists()) { + aaptFile.setExecutable(true); + cmd.add(aaptFile.getPath()); + customAapt = true; + + if (flags.get("verbose")) { + LOGGER.info(aaptFile.getPath() + " being used as aapt location."); + } + } else { + LOGGER.warning("aapt location could not be found. Defaulting back to default"); + + try { + cmd.add(getAaptBinaryFile().getAbsolutePath()); + } catch (BrutException ignored) { + cmd.add("aapt"); + } + } + } else { + try { + cmd.add(getAaptBinaryFile().getAbsolutePath()); + } catch (BrutException ignored) { + cmd.add("aapt"); + } + } + + cmd.add("p"); + + if (flags.get("verbose")) { // output aapt verbose + cmd.add("-v"); + } + if (flags.get("update")) { + cmd.add("-u"); + } + if (flags.get("debug")) { // inject debuggable="true" into manifest + cmd.add("--debug-mode"); + } + + // force package id so that some frameworks build with correct id + // disable if user adds own aapt (can't know if they have this feature) + if (mPackageId != null && customAapt == false) { + cmd.add("--forced-package-id"); + cmd.add(mPackageId); + } + if (mMinSdkVersion != null) { + cmd.add("--min-sdk-version"); + cmd.add(mMinSdkVersion); + } + if (mTargetSdkVersion != null) { + cmd.add("--target-sdk-version"); + cmd.add(mTargetSdkVersion); + } + if (mMaxSdkVersion != null) { + cmd.add("--max-sdk-version"); + cmd.add(mMaxSdkVersion); + + // if we have max sdk version, set --max-res-version + // so we can ignore anything over that during build. + cmd.add("--max-res-version"); + cmd.add(mMaxSdkVersion); + } + if (mPackageRenamed != null) { + cmd.add("--rename-manifest-package"); + cmd.add(mPackageRenamed); + } + if (mVersionCode != null) { + cmd.add("--version-code"); + cmd.add(mVersionCode); + } + if (mVersionName != null) { + cmd.add("--version-name"); + cmd.add(mVersionName); + } + cmd.add("-F"); + cmd.add(apkFile.getAbsolutePath()); + + if (flags.get("framework")) { + cmd.add("-x"); + } + + if (!(flags.get("compression"))) { + cmd.add("-0"); + cmd.add("arsc"); + } + + if (include != null) { + for (File file : include) { + cmd.add("-I"); + cmd.add(file.getPath()); + } + } + if (resDir != null) { + cmd.add("-S"); + cmd.add(resDir.getAbsolutePath()); + } + if (manifest != null) { + cmd.add("-M"); + cmd.add(manifest.getAbsolutePath()); + } + if (assetDir != null) { + cmd.add("-A"); + cmd.add(assetDir.getAbsolutePath()); + } + if (rawDir != null) { + cmd.add(rawDir.getAbsolutePath()); + } + try { + OS.exec(cmd.toArray(new String[0])); + if (flags.get("verbose")) { + LOGGER.info("command ran: "); + LOGGER.info(cmd.toString()); + } + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + } + + public boolean detectWhetherAppIsFramework(File appDir) + throws AndrolibException { + File publicXml = new File(appDir, "res/values/public.xml"); + if (!publicXml.exists()) { + return false; + } + + Iterator it; + try { + it = IOUtils.lineIterator(new FileReader(new File(appDir, + "res/values/public.xml"))); + } catch (FileNotFoundException ex) { + throw new AndrolibException( + "Could not detect whether app is framework one", ex); + } + it.next(); + it.next(); + return it.next().contains("0x01"); + } + + public void tagSmaliResIDs(ResTable resTable, File smaliDir) + throws AndrolibException { + new ResSmaliUpdater().tagResIDs(resTable, smaliDir); + } + + public void updateSmaliResIDs(ResTable resTable, File smaliDir) + throws AndrolibException { + new ResSmaliUpdater().updateResIDs(resTable, smaliDir); + } + + public Duo getResFileDecoder() { + ResStreamDecoderContainer decoders = new ResStreamDecoderContainer(); + decoders.setDecoder("raw", new ResRawStreamDecoder()); + decoders.setDecoder("9patch", new Res9patchStreamDecoder()); + + AXmlResourceParser axmlParser = new AXmlResourceParser(); + axmlParser.setAttrDecoder(new ResAttrDecoder()); + decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser, + getResXmlSerializer())); + + return new Duo(new ResFileDecoder( + decoders), axmlParser); + } + + public Duo getManifestFileDecoder() { + ResStreamDecoderContainer decoders = new ResStreamDecoderContainer(); + + AXmlResourceParser axmlParser = new AXmlResourceParser(); + + decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser, + getResXmlSerializer())); + + return new Duo(new ResFileDecoder( + decoders), axmlParser); + } + + public ExtMXSerializer getResXmlSerializer() { + ExtMXSerializer serial = new ExtMXSerializer(); + serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION, + " "); + serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR, + System.getProperty("line.separator")); + serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8"); + serial.setDisabledAttrEscape(true); + return serial; + } + + private void generateValuesFile(ResValuesFile valuesFile, Directory out, + ExtXmlSerializer serial) throws AndrolibException { + try { + OutputStream outStream = out.getFileOutput(valuesFile.getPath()); + serial.setOutput((outStream), null); + serial.startDocument(null, null); + serial.startTag(null, "resources"); + + for (ResResource res : valuesFile.listResources()) { + if (valuesFile.isSynthesized(res)) { + continue; + } + ((ResValuesXmlSerializable) res.getValue()) + .serializeToResValuesXml(serial, res); + } + + serial.endTag(null, "resources"); + serial.newLine(); + serial.endDocument(); + serial.flush(); + outStream.close(); + } catch (IOException ex) { + throw new AndrolibException("Could not generate: " + + valuesFile.getPath(), ex); + } catch (DirectoryException ex) { + throw new AndrolibException("Could not generate: " + + valuesFile.getPath(), ex); + } + } + + private void generatePublicXml(ResPackage pkg, Directory out, + XmlSerializer serial) throws AndrolibException { + try { + OutputStream outStream = out.getFileOutput("values/public.xml"); + serial.setOutput(outStream, null); + serial.startDocument(null, null); + serial.startTag(null, "resources"); + + for (ResResSpec spec : pkg.listResSpecs()) { + serial.startTag(null, "public"); + serial.attribute(null, "type", spec.getType().getName()); + serial.attribute(null, "name", spec.getName()); + serial.attribute(null, "id", + String.format("0x%08x", spec.getId().id)); + serial.endTag(null, "public"); + } + + serial.endTag(null, "resources"); + serial.endDocument(); + serial.flush(); + outStream.close(); + } catch (IOException ex) { + throw new AndrolibException("Could not generate public.xml file", + ex); + } catch (DirectoryException ex) { + throw new AndrolibException("Could not generate public.xml file", + ex); + } + } + + private ResPackage[] getResPackagesFromApk(ExtFile apkFile, + ResTable resTable, boolean keepBroken) throws AndrolibException { + try { + return ARSCDecoder.decode( + apkFile.getDirectory().getFileInput("resources.arsc"), + false, keepBroken, resTable).getPackages(); + } catch (DirectoryException ex) { + throw new AndrolibException( + "Could not load resources.arsc from file: " + apkFile, ex); + } + } + + public File getFrameworkApk(int id, String frameTag) + throws AndrolibException { + File dir = getFrameworkDir(); + File apk; + + if (frameTag != null) { + apk = new File(dir, String.valueOf(id) + '-' + frameTag + ".apk"); + if (apk.exists()) { + return apk; + } + } + + apk = new File(dir, String.valueOf(id) + ".apk"); + if (apk.exists()) { + return apk; + } + + if (id == 1) { + InputStream in = null; + OutputStream out = null; + try { + in = AndrolibResources.class + .getResourceAsStream("/brut/androlib/android-framework.jar"); + out = new FileOutputStream(apk); + IOUtils.copy(in, out); + return apk; + } catch (IOException ex) { + throw new AndrolibException(ex); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + } + } + if (out != null) { + try { + out.close(); + } catch (IOException ex) { + } + } + } + } + + throw new CantFindFrameworkResException(id); + } + + public void installFramework(File frameFile, String tag) + throws AndrolibException { + InputStream in = null; + ZipArchiveOutputStream out = null; + try { + ZipExtFile zip = new ZipExtFile(frameFile); + ZipArchiveEntry entry = zip.getEntry("resources.arsc"); + + if (entry == null) { + throw new AndrolibException("Can't find resources.arsc file"); + } + + in = zip.getInputStream(entry); + byte[] data = IOUtils.toByteArray(in); + + ARSCData arsc = ARSCDecoder.decode(new ByteArrayInputStream(data), + true, true); + publicizeResources(data, arsc.getFlagsOffsets()); + + File outFile = new File(getFrameworkDir(), String.valueOf(arsc + .getOnePackage().getId()) + + (tag == null ? "" : '-' + tag) + + ".apk"); + + out = new ZipArchiveOutputStream(new FileOutputStream(outFile)); + out.setMethod(ZipOutputStream.STORED); + CRC32 crc = new CRC32(); + crc.update(data); + entry = new ZipArchiveEntry("resources.arsc"); + entry.setSize(data.length); + entry.setCrc(crc.getValue()); + out.putArchiveEntry(entry); + out.write(data); + + out.closeArchiveEntry(); + zip.close(); + LOGGER.info("Framework installed to: " + outFile); + } catch (ZipException ex) { + throw new AndrolibException(ex); + } catch (IOException ex) { + throw new AndrolibException(ex); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + } + } + if (out != null) { + try { + out.close(); + } catch (IOException ex) { + } + } + } + } + + public void publicizeResources(File arscFile) throws AndrolibException { + byte[] data = new byte[(int) arscFile.length()]; + + InputStream in = null; + OutputStream out = null; + try { + in = new FileInputStream(arscFile); + in.read(data); + + publicizeResources(data); + + out = new FileOutputStream(arscFile); + out.write(data); + } catch (IOException ex) { + throw new AndrolibException(ex); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + } + } + if (out != null) { + try { + out.close(); + } catch (IOException ex) { + } + } + } + } + + public void publicizeResources(byte[] arsc) throws AndrolibException { + publicizeResources(arsc, + ARSCDecoder.decode(new ByteArrayInputStream(arsc), true, true) + .getFlagsOffsets()); + } + + public void publicizeResources(byte[] arsc, FlagsOffset[] flagsOffsets) + throws AndrolibException { + for (FlagsOffset flags : flagsOffsets) { + int offset = flags.offset + 3; + int end = offset + 4 * flags.count; + while (offset < end) { + arsc[offset] |= (byte) 0x40; + offset += 4; + } + } + } + + private File getFrameworkDir() throws AndrolibException { + String path; + + // if a framework path was specified on the command line, use it + if (sFrameworkFolder != null) { + path = sFrameworkFolder; + } else if (OSDetection.isMacOSX()) { + path = System.getProperty("user.home") + File.separatorChar + "Library" + File.separatorChar + "apktool" + File.separatorChar + "framework"; + } else { + path = System.getProperty("user.home") + File.separatorChar + "apktool" + File.separatorChar + "framework"; + } + + File dir = new File(path); + if (!dir.exists()) { + if (!dir.mkdirs()) { + if (sFrameworkFolder != null) { + System.err.println("Can't create Framework directory: " + + dir); + } + throw new AndrolibException("Can't create directory: " + dir); + } + } + return dir; + } + + /** + * Using a prebuilt aapt and forcing its use, allows us to prevent bugs from older aapt's + * along with having a finer control over the build procedure. + * + * Aapt can still be overridden via --aapt/-a on build, but specific features will be disabled + * + * @url https://github.com/iBotPeaches/platform_frameworks_base + * @throws AndrolibException + */ + public File getAaptBinaryFile() throws AndrolibException { + try { + if (OSDetection.isMacOSX()) { + mAaptBinary = Jar + .getResourceAsFile("/prebuilt/aapt/macosx/aapt"); + } else if (OSDetection.isUnix()) { + mAaptBinary = Jar + .getResourceAsFile("/prebuilt/aapt/linux/aapt"); + } else if (OSDetection.isWindows()) { + mAaptBinary = Jar + .getResourceAsFile("/prebuilt/aapt/windows/aapt.exe"); + } else { + LOGGER.warning("Unknown Operating System: " + OSDetection.returnOS()); + return null; + } + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + mAaptBinary.setExecutable(true); + return mAaptBinary; + } + + public File getAndroidResourcesFile() throws AndrolibException { + try { + return Jar + .getResourceAsFile("/brut/androlib/android-framework.jar"); + } catch (BrutException ex) { + throw new AndrolibException(ex); + } + } + + public void setFrameworkFolder(String path) { + sFrameworkFolder = path; + } + + // TODO: dirty static hack. I have to refactor decoding mechanisms. + public static boolean sKeepBroken = false; + public static String sFrameworkFolder = null; + + private final static Logger LOGGER = Logger + .getLogger(AndrolibResources.class.getName()); + + private String mMinSdkVersion = null; + private String mMaxSdkVersion = null; + private String mTargetSdkVersion = null; + private String mVersionCode = null; + private String mVersionName = null; + + private String mPackageRenamed = null; + private String mPackageOriginal = null; + private String mPackageId = null; + + private File mAaptBinary = null; + + private final static String[] IGNORED_PACKAGES = new String[] { + "android", "com.htc", "miui" }; + + private final static String[] ALLOWED_PACKAGES = new String[] { + "com.miui" }; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java index 22845a00..be17d824 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResSmaliUpdater.java @@ -36,128 +36,128 @@ import org.apache.commons.io.IOUtils; * @author Ryszard Wiśniewski */ public class ResSmaliUpdater { - public void tagResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - Directory dir = null; - try { - dir = new FileDirectory(smaliDir); - } catch (DirectoryException ex) { - throw new AndrolibException("Could not tag res IDs", ex); - } - for (String fileName : dir.getFiles(true)) { - try { - tagResIdsForFile(resTable, dir, fileName); - } catch (IOException ex) { - throw new AndrolibException("Could not tag resIDs for file: " - + fileName, ex); - } catch (DirectoryException ex) { - throw new AndrolibException("Could not tag resIDs for file: " - + fileName, ex); - } catch (AndrolibException ex) { - throw new AndrolibException("Could not tag resIDs for file: " - + fileName, ex); - } - } - } + public void tagResIDs(ResTable resTable, File smaliDir) + throws AndrolibException { + Directory dir = null; + try { + dir = new FileDirectory(smaliDir); + } catch (DirectoryException ex) { + throw new AndrolibException("Could not tag res IDs", ex); + } + for (String fileName : dir.getFiles(true)) { + try { + tagResIdsForFile(resTable, dir, fileName); + } catch (IOException ex) { + throw new AndrolibException("Could not tag resIDs for file: " + + fileName, ex); + } catch (DirectoryException ex) { + throw new AndrolibException("Could not tag resIDs for file: " + + fileName, ex); + } catch (AndrolibException ex) { + throw new AndrolibException("Could not tag resIDs for file: " + + fileName, ex); + } + } + } - public void updateResIDs(ResTable resTable, File smaliDir) - throws AndrolibException { - try { - Directory dir = new FileDirectory(smaliDir); - for (String fileName : dir.getFiles(true)) { - Iterator it = IOUtils.readLines( - dir.getFileInput(fileName)).iterator(); - PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); - while (it.hasNext()) { - String line = it.next(); - out.println(line); - Matcher m1 = RES_NAME_PATTERN.matcher(line); - if (!m1.matches()) { - continue; - } - Matcher m2 = RES_ID_PATTERN.matcher(it.next()); - if (!m2.matches()) { - throw new AndrolibException(); - } - int resID = resTable.getPackage(m1.group(1)) - .getType(m1.group(2)).getResSpec(m1.group(3)) - .getId().id; - if (m2.group(1) != null) { - out.println(String.format(RES_ID_FORMAT_FIELD, - m2.group(1), resID)); - } else { - out.println(String.format(RES_ID_FORMAT_CONST, - m2.group(2), resID)); - } - } - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException("Could not tag res IDs for: " - + smaliDir.getAbsolutePath(), ex); - } catch (DirectoryException ex) { - throw new AndrolibException("Could not tag res IDs for: " - + smaliDir.getAbsolutePath(), ex); - } - } + public void updateResIDs(ResTable resTable, File smaliDir) + throws AndrolibException { + try { + Directory dir = new FileDirectory(smaliDir); + for (String fileName : dir.getFiles(true)) { + Iterator it = IOUtils.readLines( + dir.getFileInput(fileName)).iterator(); + PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); + while (it.hasNext()) { + String line = it.next(); + out.println(line); + Matcher m1 = RES_NAME_PATTERN.matcher(line); + if (!m1.matches()) { + continue; + } + Matcher m2 = RES_ID_PATTERN.matcher(it.next()); + if (!m2.matches()) { + throw new AndrolibException(); + } + int resID = resTable.getPackage(m1.group(1)) + .getType(m1.group(2)).getResSpec(m1.group(3)) + .getId().id; + if (m2.group(1) != null) { + out.println(String.format(RES_ID_FORMAT_FIELD, + m2.group(1), resID)); + } else { + out.println(String.format(RES_ID_FORMAT_CONST, + m2.group(2), resID)); + } + } + out.close(); + } + } catch (IOException ex) { + throw new AndrolibException("Could not tag res IDs for: " + + smaliDir.getAbsolutePath(), ex); + } catch (DirectoryException ex) { + throw new AndrolibException("Could not tag res IDs for: " + + smaliDir.getAbsolutePath(), ex); + } + } - private void tagResIdsForFile(ResTable resTable, Directory dir, - String fileName) throws IOException, DirectoryException, - AndrolibException { - Iterator it = IOUtils.readLines(dir.getFileInput(fileName)) - .iterator(); - PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); - while (it.hasNext()) { - String line = it.next(); - if (RES_NAME_PATTERN.matcher(line).matches()) { - out.println(line); - out.println(it.next()); - continue; - } - Matcher m = RES_ID_PATTERN.matcher(line); - if (m.matches()) { - int resID = parseResID(m.group(3)); - if (resID != -1) { - try { - ResResSpec spec = resTable.getResSpec(resID); - out.println(String.format(RES_NAME_FORMAT, - spec.getFullName())); - } catch (UndefinedResObject ex) { - if (!R_FILE_PATTERN.matcher(fileName).matches()) { - LOGGER.warning(String.format( - "Undefined resource spec in %s: 0x%08x", - fileName, resID)); - } - } - } - } - out.println(line); - } - out.close(); - } + private void tagResIdsForFile(ResTable resTable, Directory dir, + String fileName) throws IOException, DirectoryException, + AndrolibException { + Iterator it = IOUtils.readLines(dir.getFileInput(fileName)) + .iterator(); + PrintWriter out = new PrintWriter(dir.getFileOutput(fileName)); + while (it.hasNext()) { + String line = it.next(); + if (RES_NAME_PATTERN.matcher(line).matches()) { + out.println(line); + out.println(it.next()); + continue; + } + Matcher m = RES_ID_PATTERN.matcher(line); + if (m.matches()) { + int resID = parseResID(m.group(3)); + if (resID != -1) { + try { + ResResSpec spec = resTable.getResSpec(resID); + out.println(String.format(RES_NAME_FORMAT, + spec.getFullName())); + } catch (UndefinedResObject ex) { + if (!R_FILE_PATTERN.matcher(fileName).matches()) { + LOGGER.warning(String.format( + "Undefined resource spec in %s: 0x%08x", + fileName, resID)); + } + } + } + } + out.println(line); + } + out.close(); + } - private int parseResID(String resIDHex) { - if (resIDHex.endsWith("ff")) { - return -1; - } - int resID = Integer.valueOf(resIDHex, 16); - if (resIDHex.length() == 4) { - resID = resID << 16; - } - return resID; - } + private int parseResID(String resIDHex) { + if (resIDHex.endsWith("ff")) { + return -1; + } + int resID = Integer.valueOf(resIDHex, 16); + if (resIDHex.length() == 4) { + resID = resID << 16; + } + return resID; + } - private final static String RES_ID_FORMAT_FIELD = ".field %s:I = 0x%08x"; - private final static String RES_ID_FORMAT_CONST = " const %s, 0x%08x"; - private final static Pattern RES_ID_PATTERN = Pattern - .compile("^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$"); - private final static String RES_NAME_FORMAT = "# APKTOOL/RES_NAME: %s"; - private final static Pattern RES_NAME_PATTERN = Pattern - .compile("^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$"); + private final static String RES_ID_FORMAT_FIELD = ".field %s:I = 0x%08x"; + private final static String RES_ID_FORMAT_CONST = " const %s, 0x%08x"; + private final static Pattern RES_ID_PATTERN = Pattern + .compile("^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$"); + private final static String RES_NAME_FORMAT = "# APKTOOL/RES_NAME: %s"; + private final static Pattern RES_NAME_PATTERN = Pattern + .compile("^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$"); - private final static Pattern R_FILE_PATTERN = Pattern - .compile(".*R\\$[a-z]+\\.smali$"); + private final static Pattern R_FILE_PATTERN = Pattern + .compile(".*R\\$[a-z]+\\.smali$"); - private final static Logger LOGGER = Logger.getLogger(ResSmaliUpdater.class - .getName()); + private final static Logger LOGGER = Logger.getLogger(ResSmaliUpdater.class + .getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java index 2d7f7bbd..73b1bcdc 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfig.java @@ -24,49 +24,49 @@ import java.util.*; * @author Ryszard Wiśniewski */ public class ResConfig { - private final ResConfigFlags mFlags; - private final Map mResources = new LinkedHashMap(); + private final ResConfigFlags mFlags; + private final Map mResources = new LinkedHashMap(); - public ResConfig(ResConfigFlags flags) { - this.mFlags = flags; - } + public ResConfig(ResConfigFlags flags) { + this.mFlags = flags; + } - public Set listResources() { - return new LinkedHashSet(mResources.values()); - } + public Set listResources() { + return new LinkedHashSet(mResources.values()); + } - public ResResource getResource(ResResSpec spec) throws AndrolibException { - ResResource res = mResources.get(spec); - if (res == null) { - throw new UndefinedResObject(String.format( - "resource: spec=%s, config=%s", spec, this)); - } - return res; - } + public ResResource getResource(ResResSpec spec) throws AndrolibException { + ResResource res = mResources.get(spec); + if (res == null) { + throw new UndefinedResObject(String.format( + "resource: spec=%s, config=%s", spec, this)); + } + return res; + } - public Set listResSpecs() { - return mResources.keySet(); - } + public Set listResSpecs() { + return mResources.keySet(); + } - public ResConfigFlags getFlags() { - return mFlags; - } + public ResConfigFlags getFlags() { + return mFlags; + } - public void addResource(ResResource res) throws AndrolibException { - addResource(res, false); - } + public void addResource(ResResource res) throws AndrolibException { + addResource(res, false); + } - public void addResource(ResResource res, boolean overwrite) - throws AndrolibException { - ResResSpec spec = res.getResSpec(); - if (mResources.put(spec, res) != null && !overwrite) { - throw new AndrolibException(String.format( - "Multiple resources: spec=%s, config=%s", spec, this)); - } - } + public void addResource(ResResource res, boolean overwrite) + throws AndrolibException { + ResResSpec spec = res.getResSpec(); + if (mResources.put(spec, res) != null && !overwrite) { + throw new AndrolibException(String.format( + "Multiple resources: spec=%s, config=%s", spec, this)); + } + } - @Override - public String toString() { - return mFlags.toString(); - } + @Override + public String toString() { + return mFlags.toString(); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java index d3ffaf2e..eef4c143 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java @@ -22,460 +22,467 @@ import java.util.logging.Logger; * @author Ryszard Wiśniewski */ public class ResConfigFlags { - public final short mcc; - public final short mnc; + public final short mcc; + public final short mnc; - public final char[] language; - public final char[] country; + public final char[] language; + public final char[] country; - public final short layoutDirection; + public final short layoutDirection; - public final byte orientation; - public final byte touchscreen; - public final short density; + public final byte orientation; + public final byte touchscreen; + public final int density; - public final byte keyboard; - public final byte navigation; - public final byte inputFlags; + public final byte keyboard; + public final byte navigation; + public final byte inputFlags; - public final short screenWidth; - public final short screenHeight; + public final short screenWidth; + public final short screenHeight; - public final short sdkVersion; + public final short sdkVersion; - public final byte screenLayout; - public final byte uiMode; - public final short smallestScreenWidthDp; + public final byte screenLayout; + public final byte uiMode; + public final short smallestScreenWidthDp; - public final short screenWidthDp; - public final short screenHeightDp; + public final short screenWidthDp; + public final short screenHeightDp; - public final boolean isInvalid; + public final boolean isInvalid; - private final String mQualifiers; + private final String mQualifiers; - public ResConfigFlags() { - mcc = 0; - mnc = 0; - language = new char[] { '\00', '\00' }; - country = new char[] { '\00', '\00' }; - layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY; - orientation = ORIENTATION_ANY; - touchscreen = TOUCHSCREEN_ANY; - density = DENSITY_DEFAULT; - keyboard = KEYBOARD_ANY; - navigation = NAVIGATION_ANY; - inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY; - screenWidth = 0; - screenHeight = 0; - sdkVersion = 0; - screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY; - uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY; - smallestScreenWidthDp = 0; - screenWidthDp = 0; - screenHeightDp = 0; - isInvalid = false; - mQualifiers = ""; - } + public ResConfigFlags() { + mcc = 0; + mnc = 0; + language = new char[] { '\00', '\00' }; + country = new char[] { '\00', '\00' }; + layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY; + orientation = ORIENTATION_ANY; + touchscreen = TOUCHSCREEN_ANY; + density = DENSITY_DEFAULT; + keyboard = KEYBOARD_ANY; + navigation = NAVIGATION_ANY; + inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY; + screenWidth = 0; + screenHeight = 0; + sdkVersion = 0; + screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY; + uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY; + smallestScreenWidthDp = 0; + screenWidthDp = 0; + screenHeightDp = 0; + isInvalid = false; + mQualifiers = ""; + } - public ResConfigFlags(short mcc, short mnc, char[] language, - char[] country, short layoutDirection, byte orientation, - byte touchscreen, short density, byte keyboard, byte navigation, - byte inputFlags, short screenWidth, short screenHeight, - short sdkVersion, byte screenLayout, byte uiMode, - short smallestScreenWidthDp, short screenWidthDp, - short screenHeightDp, boolean isInvalid) { - if (orientation < 0 || orientation > 3) { - LOGGER.warning("Invalid orientation value: " + orientation); - orientation = 0; - isInvalid = true; - } - if (touchscreen < 0 || touchscreen > 3) { - LOGGER.warning("Invalid touchscreen value: " + touchscreen); - touchscreen = 0; - isInvalid = true; - } - if (density < -1) { - LOGGER.warning("Invalid density value: " + density); - density = 0; - isInvalid = true; - } - if (keyboard < 0 || keyboard > 3) { - LOGGER.warning("Invalid keyboard value: " + keyboard); - keyboard = 0; - isInvalid = true; - } - if (navigation < 0 || navigation > 4) { - LOGGER.warning("Invalid navigation value: " + navigation); - navigation = 0; - isInvalid = true; - } + public ResConfigFlags(short mcc, short mnc, char[] language, + char[] country, short layoutDirection, byte orientation, + byte touchscreen, int density, byte keyboard, byte navigation, + byte inputFlags, short screenWidth, short screenHeight, + short sdkVersion, byte screenLayout, byte uiMode, + short smallestScreenWidthDp, short screenWidthDp, + short screenHeightDp, boolean isInvalid) { + if (orientation < 0 || orientation > 3) { + LOGGER.warning("Invalid orientation value: " + orientation); + orientation = 0; + isInvalid = true; + } + if (touchscreen < 0 || touchscreen > 3) { + LOGGER.warning("Invalid touchscreen value: " + touchscreen); + touchscreen = 0; + isInvalid = true; + } + if (density < -1) { + LOGGER.warning("Invalid density value: " + density); + density = 0; + isInvalid = true; + } + if (keyboard < 0 || keyboard > 3) { + LOGGER.warning("Invalid keyboard value: " + keyboard); + keyboard = 0; + isInvalid = true; + } + if (navigation < 0 || navigation > 4) { + LOGGER.warning("Invalid navigation value: " + navigation); + navigation = 0; + isInvalid = true; + } - this.mcc = mcc; - this.mnc = mnc; - this.language = language; - this.country = country; - this.layoutDirection = layoutDirection; - this.orientation = orientation; - this.touchscreen = touchscreen; - this.density = density; - this.keyboard = keyboard; - this.navigation = navigation; - this.inputFlags = inputFlags; - this.screenWidth = screenWidth; - this.screenHeight = screenHeight; - this.sdkVersion = sdkVersion; - this.screenLayout = screenLayout; - this.uiMode = uiMode; - this.smallestScreenWidthDp = smallestScreenWidthDp; - this.screenWidthDp = screenWidthDp; - this.screenHeightDp = screenHeightDp; - this.isInvalid = isInvalid; - mQualifiers = generateQualifiers(); - } + this.mcc = mcc; + this.mnc = mnc; + this.language = language; + this.country = country; + this.layoutDirection = layoutDirection; + this.orientation = orientation; + this.touchscreen = touchscreen; + this.density = density; + this.keyboard = keyboard; + this.navigation = navigation; + this.inputFlags = inputFlags; + this.screenWidth = screenWidth; + this.screenHeight = screenHeight; + this.sdkVersion = sdkVersion; + this.screenLayout = screenLayout; + this.uiMode = uiMode; + this.smallestScreenWidthDp = smallestScreenWidthDp; + this.screenWidthDp = screenWidthDp; + this.screenHeightDp = screenHeightDp; + this.isInvalid = isInvalid; + mQualifiers = generateQualifiers(); + } - public String getQualifiers() { - return mQualifiers; - } + public String getQualifiers() { + return mQualifiers; + } - private String generateQualifiers() { - StringBuilder ret = new StringBuilder(); - if (mcc != 0) { - ret.append("-mcc").append(String.format("%03d", mcc)); - if (mnc != 0) { - ret.append("-mnc").append(mnc); - } - } - if (language[0] != '\00') { - ret.append('-').append(language); - if (country[0] != '\00') { - ret.append("-r").append(country); - } - } - switch (screenLayout & MASK_LAYOUTDIR) { - case SCREENLAYOUT_LAYOUTDIR_RTL: - ret.append("-ldrtl"); - break; - case SCREENLAYOUT_LAYOUTDIR_LTR: - ret.append("-ldltr"); - break; - } - if (smallestScreenWidthDp != 0) { - ret.append("-sw").append(smallestScreenWidthDp).append("dp"); - } - if (screenWidthDp != 0) { - ret.append("-w").append(screenWidthDp).append("dp"); - } - if (screenHeightDp != 0) { - ret.append("-h").append(screenHeightDp).append("dp"); - } - switch (screenLayout & MASK_SCREENSIZE) { - case SCREENSIZE_SMALL: - ret.append("-small"); - break; - case SCREENSIZE_NORMAL: - ret.append("-normal"); - break; - case SCREENSIZE_LARGE: - ret.append("-large"); - break; - case SCREENSIZE_XLARGE: - ret.append("-xlarge"); - break; - } - switch (screenLayout & MASK_SCREENLONG) { - case SCREENLONG_YES: - ret.append("-long"); - break; - case SCREENLONG_NO: - ret.append("-notlong"); - break; - } - switch (orientation) { - case ORIENTATION_PORT: - ret.append("-port"); - break; - case ORIENTATION_LAND: - ret.append("-land"); - break; - case ORIENTATION_SQUARE: - ret.append("-square"); - break; - } - switch (uiMode & MASK_UI_MODE_TYPE) { - case UI_MODE_TYPE_CAR: - ret.append("-car"); - break; - case UI_MODE_TYPE_DESK: - ret.append("-desk"); - break; - case UI_MODE_TYPE_TELEVISION: - ret.append("-television"); - break; - case UI_MODE_TYPE_SMALLUI: - ret.append("-smallui"); - break; - case UI_MODE_TYPE_MEDIUMUI: - ret.append("-mediumui"); - break; - case UI_MODE_TYPE_LARGEUI: - ret.append("-largeui"); - break; - case UI_MODE_TYPE_HUGEUI: - ret.append("-hugeui"); - break; - case UI_MODE_TYPE_APPLIANCE: - ret.append("-appliance"); - break; - case UI_MODE_TYPE_INVERTED: - ret.append("-inverted"); - break; - } - switch (uiMode & MASK_UI_MODE_NIGHT) { - case UI_MODE_NIGHT_YES: - ret.append("-night"); - break; - case UI_MODE_NIGHT_NO: - ret.append("-notnight"); - break; - } - switch (density) { - case DENSITY_DEFAULT: - break; - case DENSITY_LOW: - ret.append("-ldpi"); - break; - case DENSITY_MEDIUM: - ret.append("-mdpi"); - break; - case DENSITY_HIGH: - ret.append("-hdpi"); - break; - case DENSITY_TV: - ret.append("-tvdpi"); - break; - case DENSITY_XHIGH: - ret.append("-xhdpi"); - break; - case DENSITY_XXHIGH: - ret.append("-xxhdpi"); - break; - case DENSITY_NONE: - ret.append("-nodpi"); - break; - default: - ret.append('-').append(density).append("dpi"); - } - switch (touchscreen) { - case TOUCHSCREEN_NOTOUCH: - ret.append("-notouch"); - break; - case TOUCHSCREEN_STYLUS: - ret.append("-stylus"); - break; - case TOUCHSCREEN_FINGER: - ret.append("-finger"); - break; - } - switch (inputFlags & MASK_KEYSHIDDEN) { - case KEYSHIDDEN_NO: - ret.append("-keysexposed"); - break; - case KEYSHIDDEN_YES: - ret.append("-keyshidden"); - break; - case KEYSHIDDEN_SOFT: - ret.append("-keyssoft"); - break; - } - switch (keyboard) { - case KEYBOARD_NOKEYS: - ret.append("-nokeys"); - break; - case KEYBOARD_QWERTY: - ret.append("-qwerty"); - break; - case KEYBOARD_12KEY: - ret.append("-12key"); - break; - } - switch (inputFlags & MASK_NAVHIDDEN) { - case NAVHIDDEN_NO: - ret.append("-navexposed"); - break; - case NAVHIDDEN_YES: - ret.append("-navhidden"); - break; - } - switch (navigation) { - case NAVIGATION_NONAV: - ret.append("-nonav"); - break; - case NAVIGATION_DPAD: - ret.append("-dpad"); - break; - case NAVIGATION_TRACKBALL: - ret.append("-trackball"); - break; - case NAVIGATION_WHEEL: - ret.append("-wheel"); - break; - } - if (screenWidth != 0 && screenHeight != 0) { - if (screenWidth > screenHeight) { - ret.append(String.format("-%dx%d", screenWidth, screenHeight)); - } else { - ret.append(String.format("-%dx%d", screenHeight, screenWidth)); - } - } - if (sdkVersion > getNaturalSdkVersionRequirement()) { - ret.append("-v").append(sdkVersion); - } - if (isInvalid) { - ret.append("-ERR" + sErrCounter++); - } + private String generateQualifiers() { + StringBuilder ret = new StringBuilder(); + if (mcc != 0) { + ret.append("-mcc").append(String.format("%03d", mcc)); + if (mcc != MNC_ZERO) { + if (mnc != 0 && mnc != -1) { + ret.append("-mnc").append(mnc); + } + } + } + if (language[0] != '\00') { + ret.append('-').append(language); + if (country[0] != '\00') { + ret.append("-r").append(country); + } + } + switch (screenLayout & MASK_LAYOUTDIR) { + case SCREENLAYOUT_LAYOUTDIR_RTL: + ret.append("-ldrtl"); + break; + case SCREENLAYOUT_LAYOUTDIR_LTR: + ret.append("-ldltr"); + break; + } + if (smallestScreenWidthDp != 0) { + ret.append("-sw").append(smallestScreenWidthDp).append("dp"); + } + if (screenWidthDp != 0) { + ret.append("-w").append(screenWidthDp).append("dp"); + } + if (screenHeightDp != 0) { + ret.append("-h").append(screenHeightDp).append("dp"); + } + switch (screenLayout & MASK_SCREENSIZE) { + case SCREENSIZE_SMALL: + ret.append("-small"); + break; + case SCREENSIZE_NORMAL: + ret.append("-normal"); + break; + case SCREENSIZE_LARGE: + ret.append("-large"); + break; + case SCREENSIZE_XLARGE: + ret.append("-xlarge"); + break; + } + switch (screenLayout & MASK_SCREENLONG) { + case SCREENLONG_YES: + ret.append("-long"); + break; + case SCREENLONG_NO: + ret.append("-notlong"); + break; + } + switch (orientation) { + case ORIENTATION_PORT: + ret.append("-port"); + break; + case ORIENTATION_LAND: + ret.append("-land"); + break; + case ORIENTATION_SQUARE: + ret.append("-square"); + break; + } + switch (uiMode & MASK_UI_MODE_TYPE) { + case UI_MODE_TYPE_CAR: + ret.append("-car"); + break; + case UI_MODE_TYPE_DESK: + ret.append("-desk"); + break; + case UI_MODE_TYPE_TELEVISION: + ret.append("-television"); + break; + case UI_MODE_TYPE_SMALLUI: + ret.append("-smallui"); + break; + case UI_MODE_TYPE_MEDIUMUI: + ret.append("-mediumui"); + break; + case UI_MODE_TYPE_LARGEUI: + ret.append("-largeui"); + break; + case UI_MODE_TYPE_HUGEUI: + ret.append("-hugeui"); + break; + case UI_MODE_TYPE_APPLIANCE: + ret.append("-appliance"); + break; + } + switch (uiMode & MASK_UI_MODE_NIGHT) { + case UI_MODE_NIGHT_YES: + ret.append("-night"); + break; + case UI_MODE_NIGHT_NO: + ret.append("-notnight"); + break; + } + switch (density) { + case DENSITY_DEFAULT: + break; + case DENSITY_LOW: + ret.append("-ldpi"); + break; + case DENSITY_MEDIUM: + ret.append("-mdpi"); + break; + case DENSITY_HIGH: + ret.append("-hdpi"); + break; + case DENSITY_TV: + ret.append("-tvdpi"); + break; + case DENSITY_XHIGH: + ret.append("-xhdpi"); + break; + case DENSITY_XXHIGH: + ret.append("-xxhdpi"); + break; + case DENSITY_XXXHIGH: + ret.append("-xxxhdpi"); + break; + case DENSITY_NONE: + ret.append("-nodpi"); + break; + default: + ret.append('-').append(density).append("dpi"); + } + switch (touchscreen) { + case TOUCHSCREEN_NOTOUCH: + ret.append("-notouch"); + break; + case TOUCHSCREEN_STYLUS: + ret.append("-stylus"); + break; + case TOUCHSCREEN_FINGER: + ret.append("-finger"); + break; + } + switch (inputFlags & MASK_KEYSHIDDEN) { + case KEYSHIDDEN_NO: + ret.append("-keysexposed"); + break; + case KEYSHIDDEN_YES: + ret.append("-keyshidden"); + break; + case KEYSHIDDEN_SOFT: + ret.append("-keyssoft"); + break; + } + switch (keyboard) { + case KEYBOARD_NOKEYS: + ret.append("-nokeys"); + break; + case KEYBOARD_QWERTY: + ret.append("-qwerty"); + break; + case KEYBOARD_12KEY: + ret.append("-12key"); + break; + } + switch (inputFlags & MASK_NAVHIDDEN) { + case NAVHIDDEN_NO: + ret.append("-navexposed"); + break; + case NAVHIDDEN_YES: + ret.append("-navhidden"); + break; + } + switch (navigation) { + case NAVIGATION_NONAV: + ret.append("-nonav"); + break; + case NAVIGATION_DPAD: + ret.append("-dpad"); + break; + case NAVIGATION_TRACKBALL: + ret.append("-trackball"); + break; + case NAVIGATION_WHEEL: + ret.append("-wheel"); + break; + } + if (screenWidth != 0 && screenHeight != 0) { + if (screenWidth > screenHeight) { + ret.append(String.format("-%dx%d", screenWidth, screenHeight)); + } else { + ret.append(String.format("-%dx%d", screenHeight, screenWidth)); + } + } + if (sdkVersion > getNaturalSdkVersionRequirement()) { + ret.append("-v").append(sdkVersion); + } + if (isInvalid) { + ret.append("-ERR" + sErrCounter++); + } - return ret.toString(); - } + return ret.toString(); + } - private short getNaturalSdkVersionRequirement() { - if (smallestScreenWidthDp != 0 || screenWidthDp != 0 - || screenHeightDp != 0) { - return SDK_HONEYCOMB_MR2; - } - if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != 0) { - return SDK_FROYO; - } - if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != 0 - || density != DENSITY_DEFAULT) { - return SDK_DONUT; - } - return 0; - } + private short getNaturalSdkVersionRequirement() { + if (smallestScreenWidthDp != 0 || screenWidthDp != 0 + || screenHeightDp != 0) { + return SDK_HONEYCOMB_MR2; + } + if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != UI_MODE_NIGHT_ANY) { + return SDK_FROYO; + } + if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != SCREENSIZE_ANY + || density != DENSITY_DEFAULT) { + return SDK_DONUT; + } + return 0; + } - @Override - public String toString() { - return !getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]"; - } + @Override + public String toString() { + return !getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]"; + } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResConfigFlags other = (ResConfigFlags) obj; - return this.mQualifiers.equals(other.mQualifiers); - } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResConfigFlags other = (ResConfigFlags) obj; + return this.mQualifiers.equals(other.mQualifiers); + } - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + this.mQualifiers.hashCode(); - return hash; - } + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + this.mQualifiers.hashCode(); + return hash; + } - // TODO: Dirty static hack. This counter should be a part of ResPackage, - // but it would be hard right now and this feature is very rarely used. - private static int sErrCounter = 0; + // TODO: Dirty static hack. This counter should be a part of ResPackage, + // but it would be hard right now and this feature is very rarely used. + private static int sErrCounter = 0; - public final static byte SDK_BASE = 1; - public final static byte SDK_BASE_1_1 = 2; - public final static byte SDK_CUPCAKE = 3; - public final static byte SDK_DONUT = 4; - public final static byte SDK_ECLAIR = 5; - public final static byte SDK_ECLAIR_0_1 = 6; - public final static byte SDK_ECLAIR_MR1 = 7; - public final static byte SDK_FROYO = 8; - public final static byte SDK_GINGERBREAD = 9; - public final static byte SDK_GINGERBREAD_MR1 = 10; - public final static byte SDK_HONEYCOMB = 11; - public final static byte SDK_HONEYCOMB_MR1 = 12; - public final static byte SDK_HONEYCOMB_MR2 = 13; - public final static byte SDK_ICE_CREAM_SANDWICH = 14; - public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15; - public final static byte SDK_JELLY_BEAN = 16; - public final static byte SDK_JELLY_BEAN_MR1 = 17; + public final static byte SDK_BASE = 1; + public final static byte SDK_BASE_1_1 = 2; + public final static byte SDK_CUPCAKE = 3; + public final static byte SDK_DONUT = 4; + public final static byte SDK_ECLAIR = 5; + public final static byte SDK_ECLAIR_0_1 = 6; + public final static byte SDK_ECLAIR_MR1 = 7; + public final static byte SDK_FROYO = 8; + public final static byte SDK_GINGERBREAD = 9; + public final static byte SDK_GINGERBREAD_MR1 = 10; + public final static byte SDK_HONEYCOMB = 11; + public final static byte SDK_HONEYCOMB_MR1 = 12; + public final static byte SDK_HONEYCOMB_MR2 = 13; + public final static byte SDK_ICE_CREAM_SANDWICH = 14; + public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15; + public final static byte SDK_JELLY_BEAN = 16; + public final static byte SDK_JELLY_BEAN_MR1 = 17; + public final static byte SDK_JELLY_BEAN_MR2 = 18; + public final static byte KITKAT = 19; - public final static byte ORIENTATION_ANY = 0; - public final static byte ORIENTATION_PORT = 1; - public final static byte ORIENTATION_LAND = 2; - public final static byte ORIENTATION_SQUARE = 3; + public final static byte ORIENTATION_ANY = 0; + public final static byte ORIENTATION_PORT = 1; + public final static byte ORIENTATION_LAND = 2; + public final static byte ORIENTATION_SQUARE = 3; - public final static byte TOUCHSCREEN_ANY = 0; - public final static byte TOUCHSCREEN_NOTOUCH = 1; - public final static byte TOUCHSCREEN_STYLUS = 2; - public final static byte TOUCHSCREEN_FINGER = 3; + public final static byte TOUCHSCREEN_ANY = 0; + public final static byte TOUCHSCREEN_NOTOUCH = 1; + public final static byte TOUCHSCREEN_STYLUS = 2; + public final static byte TOUCHSCREEN_FINGER = 3; - public final static short DENSITY_DEFAULT = 0; - public final static short DENSITY_LOW = 120; - public final static short DENSITY_MEDIUM = 160; - public final static short DENSITY_TV = 213; - public final static short DENSITY_HIGH = 240; - public final static short DENSITY_XHIGH = 320; - public final static short DENSITY_XXHIGH = 480; - public final static short DENSITY_NONE = -1; + public final static int DENSITY_DEFAULT = 0; + public final static int DENSITY_LOW = 120; + public final static int DENSITY_MEDIUM = 160; + public final static int DENSITY_400 = 190; + public final static int DENSITY_TV = 213; + public final static int DENSITY_HIGH = 240; + public final static int DENSITY_XHIGH = 320; + public final static int DENSITY_XXHIGH = 480; + public final static int DENSITY_XXXHIGH = 640; + public final static int DENSITY_NONE = 0xFFFF; - public final static short MASK_LAYOUTDIR = 0xc0; - public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00; - public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40; - public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80; - public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06; + public final static int MNC_ZERO = 0xFFFF; - public final static byte KEYBOARD_ANY = 0; - public final static byte KEYBOARD_NOKEYS = 1; - public final static byte KEYBOARD_QWERTY = 2; - public final static byte KEYBOARD_12KEY = 3; + public final static short MASK_LAYOUTDIR = 0xc0; + public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00; + public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40; + public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80; + public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06; - public final static byte NAVIGATION_ANY = 0; - public final static byte NAVIGATION_NONAV = 1; - public final static byte NAVIGATION_DPAD = 2; - public final static byte NAVIGATION_TRACKBALL = 3; - public final static byte NAVIGATION_WHEEL = 4; + public final static byte KEYBOARD_ANY = 0; + public final static byte KEYBOARD_NOKEYS = 1; + public final static byte KEYBOARD_QWERTY = 2; + public final static byte KEYBOARD_12KEY = 3; - public final static byte MASK_KEYSHIDDEN = 0x3; - public final static byte KEYSHIDDEN_ANY = 0x0; - public final static byte KEYSHIDDEN_NO = 0x1; - public final static byte KEYSHIDDEN_YES = 0x2; - public final static byte KEYSHIDDEN_SOFT = 0x3; + public final static byte NAVIGATION_ANY = 0; + public final static byte NAVIGATION_NONAV = 1; + public final static byte NAVIGATION_DPAD = 2; + public final static byte NAVIGATION_TRACKBALL = 3; + public final static byte NAVIGATION_WHEEL = 4; - public final static byte MASK_NAVHIDDEN = 0xc; - public final static byte NAVHIDDEN_ANY = 0x0; - public final static byte NAVHIDDEN_NO = 0x4; - public final static byte NAVHIDDEN_YES = 0x8; + public final static byte MASK_KEYSHIDDEN = 0x3; + public final static byte KEYSHIDDEN_ANY = 0x0; + public final static byte KEYSHIDDEN_NO = 0x1; + public final static byte KEYSHIDDEN_YES = 0x2; + public final static byte KEYSHIDDEN_SOFT = 0x3; - public final static byte MASK_SCREENSIZE = 0x0f; - public final static byte SCREENSIZE_ANY = 0x00; - public final static byte SCREENSIZE_SMALL = 0x01; - public final static byte SCREENSIZE_NORMAL = 0x02; - public final static byte SCREENSIZE_LARGE = 0x03; - public final static byte SCREENSIZE_XLARGE = 0x04; + public final static byte MASK_NAVHIDDEN = 0xc; + public final static byte NAVHIDDEN_ANY = 0x0; + public final static byte NAVHIDDEN_NO = 0x4; + public final static byte NAVHIDDEN_YES = 0x8; - public final static byte MASK_SCREENLONG = 0x30; - public final static byte SCREENLONG_ANY = 0x00; - public final static byte SCREENLONG_NO = 0x10; - public final static byte SCREENLONG_YES = 0x20; + public final static byte MASK_SCREENSIZE = 0x0f; + public final static byte SCREENSIZE_ANY = 0x00; + public final static byte SCREENSIZE_SMALL = 0x01; + public final static byte SCREENSIZE_NORMAL = 0x02; + public final static byte SCREENSIZE_LARGE = 0x03; + public final static byte SCREENSIZE_XLARGE = 0x04; - public final static byte MASK_UI_MODE_TYPE = 0x0f; - public final static byte UI_MODE_TYPE_ANY = 0x00; - public final static byte UI_MODE_TYPE_NORMAL = 0x01; - public final static byte UI_MODE_TYPE_DESK = 0x02; - public final static byte UI_MODE_TYPE_CAR = 0x03; - public final static byte UI_MODE_TYPE_TELEVISION = 0x04; - public final static byte UI_MODE_TYPE_APPLIANCE = 0x05; - public final static byte UI_MODE_TYPE_INVERTED = 0x45; - public final static byte UI_MODE_TYPE_SMALLUI = 0x0c; - public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d; - public final static byte UI_MODE_TYPE_LARGEUI = 0x0e; - public final static byte UI_MODE_TYPE_HUGEUI = 0x0f; + public final static byte MASK_SCREENLONG = 0x30; + public final static byte SCREENLONG_ANY = 0x00; + public final static byte SCREENLONG_NO = 0x10; + public final static byte SCREENLONG_YES = 0x20; - public final static byte MASK_UI_MODE_NIGHT = 0x30; - public final static byte UI_MODE_NIGHT_ANY = 0x00; - public final static byte UI_MODE_NIGHT_NO = 0x10; - public final static byte UI_MODE_NIGHT_YES = 0x20; + public final static byte MASK_UI_MODE_TYPE = 0x0f; + public final static byte UI_MODE_TYPE_ANY = 0x00; + public final static byte UI_MODE_TYPE_NORMAL = 0x01; + public final static byte UI_MODE_TYPE_DESK = 0x02; + public final static byte UI_MODE_TYPE_CAR = 0x03; + public final static byte UI_MODE_TYPE_TELEVISION = 0x04; + public final static byte UI_MODE_TYPE_APPLIANCE = 0x05; + public final static byte UI_MODE_TYPE_SMALLUI = 0x0c; + public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d; + public final static byte UI_MODE_TYPE_LARGEUI = 0x0e; + public final static byte UI_MODE_TYPE_HUGEUI = 0x0f; - private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class - .getName()); + public final static byte MASK_UI_MODE_NIGHT = 0x30; + public final static byte UI_MODE_NIGHT_ANY = 0x00; + public final static byte UI_MODE_NIGHT_NO = 0x10; + public final static byte UI_MODE_NIGHT_YES = 0x20; + + private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class + .getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java index 43f4ee83..24158030 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java @@ -20,51 +20,51 @@ package brut.androlib.res.data; * @author Ryszard Wiśniewski */ public class ResID { - public final int package_; - public final int type; - public final int entry; + public final int package_; + public final int type; + public final int entry; - public final int id; + public final int id; - public ResID(int package_, int type, int entry) { - this(package_, type, entry, (package_ << 24) + (type << 16) + entry); - } + public ResID(int package_, int type, int entry) { + this(package_, type, entry, (package_ << 24) + (type << 16) + entry); + } - public ResID(int id) { - this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id); - } + public ResID(int id) { + this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id); + } - public ResID(int package_, int type, int entry, int id) { - this.package_ = package_; - this.type = type; - this.entry = entry; - this.id = id; - } + public ResID(int package_, int type, int entry, int id) { + this.package_ = package_; + this.type = type; + this.entry = entry; + this.id = id; + } - @Override - public String toString() { - return String.format("0x%08x", id); - } + @Override + public String toString() { + return String.format("0x%08x", id); + } - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + this.id; - return hash; - } + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + this.id; + return hash; + } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResID other = (ResID) obj; - if (this.id != other.id) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResID other = (ResID) obj; + if (this.id != other.id) { + return false; + } + return true; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java index 0dd8d117..de0a6e21 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResPackage.java @@ -23,197 +23,207 @@ import brut.androlib.res.data.value.ResValueFactory; import brut.androlib.res.xml.ResValuesXmlSerializable; import brut.util.Duo; import java.util.*; +import java.util.logging.Logger; /** * @author Ryszard Wiśniewski */ public class ResPackage { - private final ResTable mResTable; - private final int mId; - private final String mName; - private final Map mResSpecs = new LinkedHashMap(); - private final Map mConfigs = new LinkedHashMap(); - private final Map mTypes = new LinkedHashMap(); - private final Set mSynthesizedRes = new HashSet(); + private final ResTable mResTable; + private final int mId; + private final String mName; + private final Map mResSpecs = new LinkedHashMap(); + private final Map mConfigs = new LinkedHashMap(); + private final Map mTypes = new LinkedHashMap(); + private final Set mSynthesizedRes = new HashSet(); - private ResValueFactory mValueFactory; + private ResValueFactory mValueFactory; - public ResPackage(ResTable resTable, int id, String name) { - this.mResTable = resTable; - this.mId = id; - this.mName = name; - } + public ResPackage(ResTable resTable, int id, String name) { + this.mResTable = resTable; + this.mId = id; + this.mName = name; + } - public List listResSpecs() { - return new ArrayList(mResSpecs.values()); - } + public List listResSpecs() { + return new ArrayList(mResSpecs.values()); + } - public boolean hasResSpec(ResID resID) { - return mResSpecs.containsKey(resID); - } + public boolean hasResSpec(ResID resID) { + return mResSpecs.containsKey(resID); + } - public ResResSpec getResSpec(ResID resID) throws UndefinedResObject { - ResResSpec spec = mResSpecs.get(resID); - if (spec == null) { - throw new UndefinedResObject("resource spec: " + resID.toString()); - } - return spec; - } + public ResResSpec getResSpec(ResID resID) throws UndefinedResObject { + ResResSpec spec = mResSpecs.get(resID); + if (spec == null) { + throw new UndefinedResObject("resource spec: " + resID.toString()); + } + return spec; + } - public List getConfigs() { - return new ArrayList(mConfigs.values()); - } + public List getConfigs() { + return new ArrayList(mConfigs.values()); + } - public boolean hasConfig(ResConfigFlags flags) { - return mConfigs.containsKey(flags); - } + public boolean hasConfig(ResConfigFlags flags) { + return mConfigs.containsKey(flags); + } - public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException { - ResConfig config = mConfigs.get(flags); - if (config == null) { - throw new UndefinedResObject("config: " + flags); - } - return config; - } + public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException { + ResConfig config = mConfigs.get(flags); + if (config == null) { + throw new UndefinedResObject("config: " + flags); + } + return config; + } - public ResConfig getOrCreateConfig(ResConfigFlags flags) - throws AndrolibException { - ResConfig config = mConfigs.get(flags); - if (config == null) { - config = new ResConfig(flags); - mConfigs.put(flags, config); - } - return config; - } + public int getResSpecCount() { + return mResSpecs.size(); + } - public List listTypes() { - return new ArrayList(mTypes.values()); - } + public ResConfig getOrCreateConfig(ResConfigFlags flags) + throws AndrolibException { + ResConfig config = mConfigs.get(flags); + if (config == null) { + config = new ResConfig(flags); + mConfigs.put(flags, config); + } + return config; + } - public boolean hasType(String typeName) { - return mTypes.containsKey(typeName); - } + public List listTypes() { + return new ArrayList(mTypes.values()); + } - public ResType getType(String typeName) throws AndrolibException { - ResType type = mTypes.get(typeName); - if (type == null) { - throw new UndefinedResObject("type: " + typeName); - } - return type; - } + public boolean hasType(String typeName) { + return mTypes.containsKey(typeName); + } - public Set listFiles() { - Set ret = new HashSet(); - for (ResResSpec spec : mResSpecs.values()) { - for (ResResource res : spec.listResources()) { - if (res.getValue() instanceof ResFileValue) { - ret.add(res); - } - } - } - return ret; - } + public ResType getType(String typeName) throws AndrolibException { + ResType type = mTypes.get(typeName); + if (type == null) { + throw new UndefinedResObject("type: " + typeName); + } + return type; + } - public Collection listValuesFiles() { - Map, ResValuesFile> ret = new HashMap, ResValuesFile>(); - for (ResResSpec spec : mResSpecs.values()) { - for (ResResource res : spec.listResources()) { - if (res.getValue() instanceof ResValuesXmlSerializable) { - ResType type = res.getResSpec().getType(); - ResConfig config = res.getConfig(); - Duo key = new Duo( - type, config); - ResValuesFile values = ret.get(key); - if (values == null) { - values = new ResValuesFile(this, type, config); - ret.put(key, values); - } - values.addResource(res); - } - } - } - return ret.values(); - } + public Set listFiles() { + Set ret = new HashSet(); + for (ResResSpec spec : mResSpecs.values()) { + for (ResResource res : spec.listResources()) { + if (res.getValue() instanceof ResFileValue) { + ret.add(res); + } + } + } + return ret; + } - public ResTable getResTable() { - return mResTable; - } + public Collection listValuesFiles() { + Map, ResValuesFile> ret = new HashMap, ResValuesFile>(); + for (ResResSpec spec : mResSpecs.values()) { + for (ResResource res : spec.listResources()) { + if (res.getValue() instanceof ResValuesXmlSerializable) { + ResType type = res.getResSpec().getType(); + ResConfig config = res.getConfig(); + Duo key = new Duo( + type, config); + ResValuesFile values = ret.get(key); + if (values == null) { + values = new ResValuesFile(this, type, config); + ret.put(key, values); + } + values.addResource(res); + } + } + } + return ret.values(); + } - public int getId() { - return mId; - } + public ResTable getResTable() { + return mResTable; + } - public String getName() { - return mName; - } + public int getId() { + return mId; + } - boolean isSynthesized(ResID resId) { - return mSynthesizedRes.contains(resId); - } + public String getName() { + return mName; + } - public void addResSpec(ResResSpec spec) throws AndrolibException { - if (mResSpecs.put(spec.getId(), spec) != null) { - throw new AndrolibException("Multiple resource specs: " + spec); - } - } + boolean isSynthesized(ResID resId) { + return mSynthesizedRes.contains(resId); + } - public void addConfig(ResConfig config) throws AndrolibException { - if (mConfigs.put(config.getFlags(), config) != null) { - throw new AndrolibException("Multiple configs: " + config); - } - } + public void addResSpec(ResResSpec spec) throws AndrolibException { + if (mResSpecs.put(spec.getId(), spec) != null) { + throw new AndrolibException("Multiple resource specs: " + spec); + } + } - public void addType(ResType type) throws AndrolibException { - if (mTypes.put(type.getName(), type) != null) { - throw new AndrolibException("Multiple types: " + type); - } - } + public void addConfig(ResConfig config) throws AndrolibException { + if (mConfigs.put(config.getFlags(), config) != null) { + throw new AndrolibException("Multiple configs: " + config); + } + } - public void addResource(ResResource res) { - } + public void addType(ResType type) throws AndrolibException { + if (mTypes.containsKey(type.getName())) { + LOGGER.warning("Multiple types detected! " + type + " ignored!"); + } else { + mTypes.put(type.getName(), type); + } + } - public void addSynthesizedRes(int resId) { - mSynthesizedRes.add(new ResID(resId)); - } + public void addResource(ResResource res) { + } - @Override - public String toString() { - return mName; - } + public void addSynthesizedRes(int resId) { + mSynthesizedRes.add(new ResID(resId)); + } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResPackage other = (ResPackage) obj; - if (this.mResTable != other.mResTable - && (this.mResTable == null || !this.mResTable - .equals(other.mResTable))) { - return false; - } - if (this.mId != other.mId) { - return false; - } - return true; - } + @Override + public String toString() { + return mName; + } - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash - + (this.mResTable != null ? this.mResTable.hashCode() : 0); - hash = 31 * hash + this.mId; - return hash; - } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResPackage other = (ResPackage) obj; + if (this.mResTable != other.mResTable + && (this.mResTable == null || !this.mResTable + .equals(other.mResTable))) { + return false; + } + if (this.mId != other.mId) { + return false; + } + return true; + } - public ResValueFactory getValueFactory() { - if (mValueFactory == null) { - mValueFactory = new ResValueFactory(this); - } - return mValueFactory; - } + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + + (this.mResTable != null ? this.mResTable.hashCode() : 0); + hash = 31 * hash + this.mId; + return hash; + } + + public ResValueFactory getValueFactory() { + if (mValueFactory == null) { + mValueFactory = new ResValueFactory(this); + } + return mValueFactory; + } + + private final static Logger LOGGER = Logger + .getLogger(ResPackage.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java index 658c9166..013d4454 100755 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResSpec.java @@ -25,97 +25,97 @@ import org.apache.commons.lang3.StringUtils; * @author Ryszard Wiśniewski */ public class ResResSpec { - private final ResID mId; - private final String mName; - private final ResPackage mPackage; - private final ResType mType; - private final Map mResources = new LinkedHashMap(); + private final ResID mId; + private final String mName; + private final ResPackage mPackage; + private final ResType mType; + private final Map mResources = new LinkedHashMap(); - public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) { - this.mId = id; - this.mName = name; - this.mPackage = pkg; - this.mType = type; - } + public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) { + this.mId = id; + this.mName = name; + this.mPackage = pkg; + this.mType = type; + } - public Set listResources() { - return new LinkedHashSet(mResources.values()); - } + public Set listResources() { + return new LinkedHashSet(mResources.values()); + } - public ResResource getResource(ResConfig config) throws AndrolibException { - return getResource(config.getFlags()); - } + public ResResource getResource(ResConfig config) throws AndrolibException { + return getResource(config.getFlags()); + } - public ResResource getResource(ResConfigFlags config) - throws AndrolibException { - ResResource res = mResources.get(config); - if (res == null) { - throw new UndefinedResObject(String.format( - "resource: spec=%s, config=%s", this, config)); - } - return res; - } + public ResResource getResource(ResConfigFlags config) + throws AndrolibException { + ResResource res = mResources.get(config); + if (res == null) { + throw new UndefinedResObject(String.format( + "resource: spec=%s, config=%s", this, config)); + } + return res; + } - public boolean hasResource(ResConfig config) { - return hasResource(config.getFlags()); - } + public boolean hasResource(ResConfig config) { + return hasResource(config.getFlags()); + } - private boolean hasResource(ResConfigFlags flags) { - return mResources.containsKey(flags); - } + private boolean hasResource(ResConfigFlags flags) { + return mResources.containsKey(flags); + } - public ResResource getDefaultResource() throws AndrolibException { - return getResource(new ResConfigFlags()); - } + public ResResource getDefaultResource() throws AndrolibException { + return getResource(new ResConfigFlags()); + } - public boolean hasDefaultResource() { - return mResources.containsKey(new ResConfigFlags()); - } + public boolean hasDefaultResource() { + return mResources.containsKey(new ResConfigFlags()); + } - public String getFullName() { - return getFullName(false, false); - } + public String getFullName() { + return getFullName(false, false); + } - public String getFullName(ResPackage relativeToPackage, boolean excludeType) { - return getFullName(getPackage().equals(relativeToPackage), excludeType); - } + public String getFullName(ResPackage relativeToPackage, boolean excludeType) { + return getFullName(getPackage().equals(relativeToPackage), excludeType); + } - public String getFullName(boolean excludePackage, boolean excludeType) { - return (excludePackage ? "" : getPackage().getName() + ":") - + (excludeType ? "" : getType().getName() + "/") + getName(); - } + public String getFullName(boolean excludePackage, boolean excludeType) { + return (excludePackage ? "" : getPackage().getName() + ":") + + (excludeType ? "" : getType().getName() + "/") + getName(); + } - public ResID getId() { - return mId; - } + public ResID getId() { + return mId; + } - public String getName() { - return StringUtils.replace(mName, "\"", "q"); - } + public String getName() { + return StringUtils.replace(mName, "\"", "q"); + } - public ResPackage getPackage() { - return mPackage; - } + public ResPackage getPackage() { + return mPackage; + } - public ResType getType() { - return mType; - } + public ResType getType() { + return mType; + } - public void addResource(ResResource res) throws AndrolibException { - addResource(res, false); - } + public void addResource(ResResource res) throws AndrolibException { + addResource(res, false); + } - public void addResource(ResResource res, boolean overwrite) - throws AndrolibException { - ResConfigFlags flags = res.getConfig().getFlags(); - if (mResources.put(flags, res) != null && !overwrite) { - throw new AndrolibException(String.format( - "Multiple resources: spec=%s, config=%s", this, flags)); - } - } + public void addResource(ResResource res, boolean overwrite) + throws AndrolibException { + ResConfigFlags flags = res.getConfig().getFlags(); + if (mResources.put(flags, res) != null && !overwrite) { + throw new AndrolibException(String.format( + "Multiple resources: spec=%s, config=%s", this, flags)); + } + } - @Override - public String toString() { - return mId.toString() + " " + mType.toString() + "/" + mName; - } + @Override + public String toString() { + return mId.toString() + " " + mType.toString() + "/" + mName; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java index 38c35d68..a9301dbf 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResResource.java @@ -23,41 +23,41 @@ import brut.androlib.res.data.value.ResValue; * @author Ryszard Wiśniewski */ public class ResResource { - private final ResConfig mConfig; - private final ResResSpec mResSpec; - private final ResValue mValue; + private final ResConfig mConfig; + private final ResResSpec mResSpec; + private final ResValue mValue; - public ResResource(ResConfig config, ResResSpec spec, ResValue value) { - this.mConfig = config; - this.mResSpec = spec; - this.mValue = value; - } + public ResResource(ResConfig config, ResResSpec spec, ResValue value) { + this.mConfig = config; + this.mResSpec = spec; + this.mValue = value; + } - public String getFilePath() { - return mResSpec.getType().getName() - + mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName(); - } + public String getFilePath() { + return mResSpec.getType().getName() + + mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName(); + } - public ResConfig getConfig() { - return mConfig; - } + public ResConfig getConfig() { + return mConfig; + } - public ResResSpec getResSpec() { - return mResSpec; - } + public ResResSpec getResSpec() { + return mResSpec; + } - public ResValue getValue() { - return mValue; - } + public ResValue getValue() { + return mValue; + } - public void replace(ResValue value) throws AndrolibException { - ResResource res = new ResResource(mConfig, mResSpec, value); - mConfig.addResource(res, true); - mResSpec.addResource(res, true); - } + public void replace(ResValue value) throws AndrolibException { + ResResource res = new ResResource(mConfig, mResSpec, value); + mConfig.addResource(res, true); + mResSpec.addResource(res, true); + } - @Override - public String toString() { - return getFilePath(); - } + @Override + public String toString() { + return getFilePath(); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java index b3c752d0..22b727c4 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java @@ -26,121 +26,176 @@ import java.util.*; * @author Ryszard Wiśniewski */ public class ResTable { - private final AndrolibResources mAndRes; + private final AndrolibResources mAndRes; - private final Map mPackagesById = new HashMap(); - private final Map mPackagesByName = new HashMap(); - private final Set mMainPackages = new LinkedHashSet(); - private final Set mFramePackages = new LinkedHashSet(); + private final Map mPackagesById = new HashMap(); + private final Map mPackagesByName = new HashMap(); + private final Set mMainPackages = new LinkedHashSet(); + private final Set mFramePackages = new LinkedHashSet(); - private String mFrameTag; + private String mFrameTag; + private String mPackageRenamed; + private String mPackageOriginal; + private int mPackageId; + private boolean mAnalysisMode = false; - private Map mSdkInfo = new LinkedHashMap(); - private Map mPackageInfo = new LinkedHashMap(); + private Map mSdkInfo = new LinkedHashMap(); + private Map mVersionInfo = new LinkedHashMap(); - public ResTable() { - mAndRes = null; - } + public ResTable() { + mAndRes = null; + } - public ResTable(AndrolibResources andRes) { - mAndRes = andRes; - } + public ResTable(AndrolibResources andRes) { + mAndRes = andRes; + } - public ResResSpec getResSpec(int resID) throws AndrolibException { - return getResSpec(new ResID(resID)); - } + public ResResSpec getResSpec(int resID) throws AndrolibException { + return getResSpec(new ResID(resID)); + } - public ResResSpec getResSpec(ResID resID) throws AndrolibException { - return getPackage(resID.package_).getResSpec(resID); - } + public ResResSpec getResSpec(ResID resID) throws AndrolibException { + return getPackage(resID.package_).getResSpec(resID); + } - public Set listMainPackages() { - return mMainPackages; - } + public Set listMainPackages() { + return mMainPackages; + } - public Set listFramePackages() { - return mFramePackages; - } + public Set listFramePackages() { + return mFramePackages; + } - public ResPackage getPackage(int id) throws AndrolibException { - ResPackage pkg = mPackagesById.get(id); - if (pkg != null) { - return pkg; - } - if (mAndRes != null) { - return mAndRes.loadFrameworkPkg(this, id, mFrameTag); - } - throw new UndefinedResObject(String.format("package: id=%d", id)); - } + public ResPackage getPackage(int id) throws AndrolibException { + ResPackage pkg = mPackagesById.get(id); + if (pkg != null) { + return pkg; + } + if (mAndRes != null) { + return mAndRes.loadFrameworkPkg(this, id, mFrameTag); + } + throw new UndefinedResObject(String.format("package: id=%d", id)); + } - public ResPackage getPackage(String name) throws AndrolibException { - ResPackage pkg = mPackagesByName.get(name); - if (pkg == null) { - throw new UndefinedResObject("package: name=" + name); - } - return pkg; - } + public ResPackage getHighestSpecPackage() throws AndrolibException { + int id = 0; + int value = 0; + for(ResPackage resPackage : mPackagesById.values()) { + if(resPackage.getResSpecCount() > value && !resPackage.getName().equalsIgnoreCase("android")) { + value = resPackage.getResSpecCount(); + id = resPackage.getId(); + } + } + // if id is still 0, we only have one pkgId which is "android" -> 1 + return (id == 0) ? getPackage(1) : getPackage(id); + } - public boolean hasPackage(int id) { - return mPackagesById.containsKey(id); - } + public ResPackage getCurrentResPackage() throws AndrolibException { + ResPackage pkg = mPackagesById.get(mPackageId); - public boolean hasPackage(String name) { - return mPackagesByName.containsKey(name); - } + if (pkg != null) { + return pkg; + } else { + return getHighestSpecPackage(); + } + } - public ResValue getValue(String package_, String type, String name) - throws AndrolibException { - return getPackage(package_).getType(type).getResSpec(name) - .getDefaultResource().getValue(); - } + public ResPackage getPackage(String name) throws AndrolibException { + ResPackage pkg = mPackagesByName.get(name); + if (pkg == null) { + throw new UndefinedResObject("package: name=" + name); + } + return pkg; + } - public void addPackage(ResPackage pkg, boolean main) - throws AndrolibException { - Integer id = pkg.getId(); - if (mPackagesById.containsKey(id)) { - throw new AndrolibException("Multiple packages: id=" - + id.toString()); - } - String name = pkg.getName(); - if (mPackagesByName.containsKey(name)) { - throw new AndrolibException("Multiple packages: name=" + name); - } + public boolean hasPackage(int id) { + return mPackagesById.containsKey(id); + } - mPackagesById.put(id, pkg); - mPackagesByName.put(name, pkg); - if (main) { - mMainPackages.add(pkg); - } else { - mFramePackages.add(pkg); - } - } + public boolean hasPackage(String name) { + return mPackagesByName.containsKey(name); + } - public void setFrameTag(String tag) { - mFrameTag = tag; - } + public ResValue getValue(String package_, String type, String name) + throws AndrolibException { + return getPackage(package_).getType(type).getResSpec(name) + .getDefaultResource().getValue(); + } - public Map getSdkInfo() { - return mSdkInfo; - } + public void addPackage(ResPackage pkg, boolean main) + throws AndrolibException { + Integer id = pkg.getId(); + if (mPackagesById.containsKey(id)) { + throw new AndrolibException("Multiple packages: id=" + + id.toString()); + } + String name = pkg.getName(); + if (mPackagesByName.containsKey(name)) { + throw new AndrolibException("Multiple packages: name=" + name); + } - public void addSdkInfo(String key, String value) { - mSdkInfo.put(key, value); - } + mPackagesById.put(id, pkg); + mPackagesByName.put(name, pkg); + if (main) { + mMainPackages.add(pkg); + } else { + mFramePackages.add(pkg); + } + } - public void clearSdkInfo() { - mSdkInfo.clear(); - } + public void setFrameTag(String tag) { + mFrameTag = tag; + } - public void addPackageInfo(String key, String value) { - mPackageInfo.put(key, value); - } + public void setAnalysisMode(boolean mode) { + mAnalysisMode = mode; + } - public Map getPackageInfo() { - return mPackageInfo; - } + public void setPackageRenamed(String pkg) { + mPackageRenamed = pkg; + } - public boolean isPackageInfoValueSet(String key) { - return (mPackageInfo.containsKey(key)); - } + public void setPackageOriginal(String pkg) { + mPackageOriginal = pkg; + } + + public void setPackageId(int id) { + mPackageId = id; + } + + public void clearSdkInfo() { + mSdkInfo.clear(); + } + + public void addSdkInfo(String key, String value) { + mSdkInfo.put(key, value); + } + + public void addVersionInfo(String key, String value) { + mVersionInfo.put(key, value); + } + + public Map getVersionInfo() { + return mVersionInfo; + } + + public Map getSdkInfo() { + return mSdkInfo; + } + + public boolean getAnalysisMode() { + return mAnalysisMode; + } + + public String getPackageRenamed() { + return mPackageRenamed; + } + + public String getPackageOriginal() { + return mPackageOriginal; + } + + public int getPackageId() { + return mPackageId; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java index e87615bf..3624b965 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResType.java @@ -24,44 +24,44 @@ import java.util.*; * @author Ryszard Wiśniewski */ public final class ResType { - private final String mName; - private final Map mResSpecs = new LinkedHashMap(); + private final String mName; + private final Map mResSpecs = new LinkedHashMap(); - private final ResTable mResTable; - private final ResPackage mPackage; + private final ResTable mResTable; + private final ResPackage mPackage; - public ResType(String name, ResTable resTable, ResPackage package_) { - this.mName = name; - this.mResTable = resTable; - this.mPackage = package_; - } + public ResType(String name, ResTable resTable, ResPackage package_) { + this.mName = name; + this.mResTable = resTable; + this.mPackage = package_; + } - public String getName() { - return mName; - } + public String getName() { + return mName; + } - public Set listResSpecs() { - return new LinkedHashSet(mResSpecs.values()); - } + public Set listResSpecs() { + return new LinkedHashSet(mResSpecs.values()); + } - public ResResSpec getResSpec(String name) throws AndrolibException { - ResResSpec spec = mResSpecs.get(name); - if (spec == null) { - throw new UndefinedResObject(String.format("resource spec: %s/%s", - getName(), name)); - } - return spec; - } + public ResResSpec getResSpec(String name) throws AndrolibException { + ResResSpec spec = mResSpecs.get(name); + if (spec == null) { + throw new UndefinedResObject(String.format("resource spec: %s/%s", + getName(), name)); + } + return spec; + } - public void addResSpec(ResResSpec spec) throws AndrolibException { - if (mResSpecs.put(spec.getName(), spec) != null) { - throw new AndrolibException(String.format( - "Multiple res specs: %s/%s", getName(), spec.getName())); - } - } + public void addResSpec(ResResSpec spec) throws AndrolibException { + if (mResSpecs.put(spec.getName(), spec) != null) { + throw new AndrolibException(String.format( + "Multiple res specs: %s/%s", getName(), spec.getName())); + } + } - @Override - public String toString() { - return mName; - } + @Override + public String toString() { + return mName; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResUnknownFiles.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResUnknownFiles.java new file mode 100644 index 00000000..7518c16a --- /dev/null +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResUnknownFiles.java @@ -0,0 +1,35 @@ +/** + * Copyright 2011 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib.res.data; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Connor Tumbleson + */ +public class ResUnknownFiles { + + private final Map mUnknownFiles = new LinkedHashMap(); + + public void addUnknownFileInfo(String file, String value) { + mUnknownFiles.put(file,value); + } + + public Map getUnknownFiles() { + return mUnknownFiles; + } +} diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java index c1be40c8..4c4af0a6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResValuesFile.java @@ -23,68 +23,68 @@ import java.util.Set; * @author Ryszard Wiśniewski */ public class ResValuesFile { - private final ResPackage mPackage; - private final ResType mType; - private final ResConfig mConfig; - private final Set mResources = new LinkedHashSet(); + private final ResPackage mPackage; + private final ResType mType; + private final ResConfig mConfig; + private final Set mResources = new LinkedHashSet(); - public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) { - this.mPackage = pkg; - this.mType = type; - this.mConfig = config; - } + public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) { + this.mPackage = pkg; + this.mType = type; + this.mConfig = config; + } - public String getPath() { - return "values" + mConfig.getFlags().getQualifiers() + "/" - + mType.getName() + (mType.getName().endsWith("s") ? "" : "s") - + ".xml"; - } + public String getPath() { + return "values" + mConfig.getFlags().getQualifiers() + "/" + + mType.getName() + (mType.getName().endsWith("s") ? "" : "s") + + ".xml"; + } - public Set listResources() { - return mResources; - } + public Set listResources() { + return mResources; + } - public ResType getType() { - return mType; - } + public ResType getType() { + return mType; + } - public ResConfig getConfig() { - return mConfig; - } + public ResConfig getConfig() { + return mConfig; + } - public boolean isSynthesized(ResResource res) { - return mPackage.isSynthesized(res.getResSpec().getId()); - } + public boolean isSynthesized(ResResource res) { + return mPackage.isSynthesized(res.getResSpec().getId()); + } - public void addResource(ResResource res) { - mResources.add(res); - } + public void addResource(ResResource res) { + mResources.add(res); + } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ResValuesFile other = (ResValuesFile) obj; - if (this.mType != other.mType - && (this.mType == null || !this.mType.equals(other.mType))) { - return false; - } - if (this.mConfig != other.mConfig - && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) { - return false; - } - return true; - } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResValuesFile other = (ResValuesFile) obj; + if (this.mType != other.mType + && (this.mType == null || !this.mType.equals(other.mType))) { + return false; + } + if (this.mConfig != other.mConfig + && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) { + return false; + } + return true; + } - @Override - public int hashCode() { - int hash = 17; - hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0); - hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0); - return hash; - } + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0); + hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0); + return hash; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java index a35791be..749cc054 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java @@ -21,71 +21,73 @@ import brut.androlib.res.data.ResResource; import brut.androlib.res.xml.ResValuesXmlSerializable; import brut.util.Duo; import java.io.IOException; +import java.util.Arrays; + import org.xmlpull.v1.XmlSerializer; /** * @author Ryszard Wiśniewski */ public class ResArrayValue extends ResBagValue implements - ResValuesXmlSerializable { - private String mRawItems; + ResValuesXmlSerializable { + private String mRawItems; - ResArrayValue(ResReferenceValue parent, Duo[] items) { - super(parent); + ResArrayValue(ResReferenceValue parent, Duo[] items) { + super(parent); - mItems = new ResScalarValue[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = items[i].m2; - } - } + mItems = new ResScalarValue[items.length]; + for (int i = 0; i < items.length; i++) { + mItems[i] = items[i].m2; + } + } - public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) { - super(parent); - mItems = items; - } + public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) { + super(parent); + mItems = items; + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException { - String type = getType(); - type = (type == null ? "" : type + "-") + "array"; - // reference array (04 10 2012, BurgerZ) - if ("reference-array".equals(type)) { - type = "string-array"; - } - // reference array (04 10 2012, BurgerZ) - serializer.startTag(null, type); - serializer.attribute(null, "name", res.getResSpec().getName()); - for (int i = 0; i < mItems.length; i++) { - serializer.startTag(null, "item"); - serializer.text(mItems[i].encodeAsResXmlItemValue()); - serializer.endTag(null, "item"); - } - serializer.endTag(null, type); - } + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + String type = getType(); + type = (type == null ? "" : type + "-") + "array"; + serializer.startTag(null, type); + serializer.attribute(null, "name", res.getResSpec().getName()); + for (int i = 0; i < mItems.length; i++) { + serializer.startTag(null, "item"); + serializer.text(mItems[i].encodeAsResXmlNonEscapedItemValue()); + serializer.endTag(null, "item"); + } + serializer.endTag(null, type); + } - public String getType() throws AndrolibException { - if (mItems.length == 0) { - return null; - } - String type = mItems[0].getType(); - for (int i = 1; i < mItems.length; i++) { + public String getType() throws AndrolibException { + if (mItems.length == 0) { + return null; + } + String type = mItems[0].getType(); + for (int i = 1; i < mItems.length; i++) { - if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) { - return "string"; - } else if (mItems[i].encodeAsResXmlItemValue().startsWith( - "@drawable")) { - return null; - } else if (!"string".equals(type) && !"integer".equals(type)) { - return null; - } else if (!type.equals(mItems[i].getType())) { - return null; - } - } - return type; - } + if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) { + return "string"; + } else if (mItems[i].encodeAsResXmlItemValue().startsWith("@drawable")) { + return null; + } else if (mItems[i].encodeAsResXmlItemValue().startsWith("@integer")) { + return "integer"; + } else if (!"string".equals(type) && !"integer".equals(type)) { + return null; + } else if (!type.equals(mItems[i].getType())) { + return null; + } + } + if (!Arrays.asList(AllowedArrayTypes).contains(type)) { + return "string"; + } + return type; + } - private final ResScalarValue[] mItems; + private final ResScalarValue[] mItems; + private final String AllowedArrayTypes[] = {"string", "integer"}; - public static final int BAG_KEY_ARRAY_START = 0x02000000; + public static final int BAG_KEY_ARRAY_START = 0x02000000; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java index 3fac36d5..e35664c8 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResAttr.java @@ -28,148 +28,148 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResAttr extends ResBagValue implements ResValuesXmlSerializable { - ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, - Boolean l10n) { - super(parentVal); - mType = type; - mMin = min; - mMax = max; - mL10n = l10n; - } + ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, + Boolean l10n) { + super(parentVal); + mType = type; + mMin = min; + mMax = max; + mL10n = l10n; + } - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - return null; - } + public String convertToResXmlFormat(ResScalarValue value) + throws AndrolibException { + return null; + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException { - String type = getTypeAsString(); + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + String type = getTypeAsString(); - serializer.startTag(null, "attr"); - serializer.attribute(null, "name", res.getResSpec().getName()); - if (type != null) { - serializer.attribute(null, "format", type); - } - if (mMin != null) { - serializer.attribute(null, "min", mMin.toString()); - } - if (mMax != null) { - serializer.attribute(null, "max", mMax.toString()); - } - if (mL10n != null && mL10n) { - serializer.attribute(null, "localization", "suggested"); - } - serializeBody(serializer, res); - serializer.endTag(null, "attr"); - } + serializer.startTag(null, "attr"); + serializer.attribute(null, "name", res.getResSpec().getName()); + if (type != null) { + serializer.attribute(null, "format", type); + } + if (mMin != null) { + serializer.attribute(null, "min", mMin.toString()); + } + if (mMax != null) { + serializer.attribute(null, "max", mMax.toString()); + } + if (mL10n != null && mL10n) { + serializer.attribute(null, "localization", "suggested"); + } + serializeBody(serializer, res); + serializer.endTag(null, "attr"); + } - public static ResAttr factory(ResReferenceValue parent, - Duo[] items, ResValueFactory factory, - ResPackage pkg) throws AndrolibException { + public static ResAttr factory(ResReferenceValue parent, + Duo[] items, ResValueFactory factory, + ResPackage pkg) throws AndrolibException { - int type = ((ResIntValue) items[0].m2).getValue(); - int scalarType = type & 0xffff; - Integer min = null, max = null; - Boolean l10n = null; - int i; - for (i = 1; i < items.length; i++) { - switch (items[i].m1) { - case BAG_KEY_ATTR_MIN: - min = ((ResIntValue) items[i].m2).getValue(); - continue; - case BAG_KEY_ATTR_MAX: - max = ((ResIntValue) items[i].m2).getValue(); - continue; - case BAG_KEY_ATTR_L10N: - l10n = ((ResIntValue) items[i].m2).getValue() != 0; - continue; - } - break; - } + int type = ((ResIntValue) items[0].m2).getValue(); + int scalarType = type & 0xffff; + Integer min = null, max = null; + Boolean l10n = null; + int i; + for (i = 1; i < items.length; i++) { + switch (items[i].m1) { + case BAG_KEY_ATTR_MIN: + min = ((ResIntValue) items[i].m2).getValue(); + continue; + case BAG_KEY_ATTR_MAX: + max = ((ResIntValue) items[i].m2).getValue(); + continue; + case BAG_KEY_ATTR_L10N: + l10n = ((ResIntValue) items[i].m2).getValue() != 0; + continue; + } + break; + } - if (i == items.length) { - return new ResAttr(parent, scalarType, min, max, l10n); - } - Duo[] attrItems = new Duo[items.length - - i]; - int j = 0; - for (; i < items.length; i++) { - int resId = items[i].m1; - pkg.addSynthesizedRes(resId); - attrItems[j++] = new Duo( - factory.newReference(resId, null), - (ResIntValue) items[i].m2); - } - switch (type & 0xff0000) { - case TYPE_ENUM: - return new ResEnumAttr(parent, scalarType, min, max, l10n, - attrItems); - case TYPE_FLAGS: - return new ResFlagsAttr(parent, scalarType, min, max, l10n, - attrItems); - } + if (i == items.length) { + return new ResAttr(parent, scalarType, min, max, l10n); + } + Duo[] attrItems = new Duo[items.length + - i]; + int j = 0; + for (; i < items.length; i++) { + int resId = items[i].m1; + pkg.addSynthesizedRes(resId); + attrItems[j++] = new Duo( + factory.newReference(resId, null), + (ResIntValue) items[i].m2); + } + switch (type & 0xff0000) { + case TYPE_ENUM: + return new ResEnumAttr(parent, scalarType, min, max, l10n, + attrItems); + case TYPE_FLAGS: + return new ResFlagsAttr(parent, scalarType, min, max, l10n, + attrItems); + } - throw new AndrolibException("Could not decode attr value"); - } + throw new AndrolibException("Could not decode attr value"); + } - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { - } + protected void serializeBody(XmlSerializer serializer, ResResource res) + throws AndrolibException, IOException { + } - protected String getTypeAsString() { - String s = ""; - if ((mType & TYPE_REFERENCE) != 0) { - s += "|reference"; - } - if ((mType & TYPE_STRING) != 0) { - s += "|string"; - } - if ((mType & TYPE_INT) != 0) { - s += "|integer"; - } - if ((mType & TYPE_BOOL) != 0) { - s += "|boolean"; - } - if ((mType & TYPE_COLOR) != 0) { - s += "|color"; - } - if ((mType & TYPE_FLOAT) != 0) { - s += "|float"; - } - if ((mType & TYPE_DIMEN) != 0) { - s += "|dimension"; - } - if ((mType & TYPE_FRACTION) != 0) { - s += "|fraction"; - } - if (s.isEmpty()) { - return null; - } - return s.substring(1); - } + protected String getTypeAsString() { + String s = ""; + if ((mType & TYPE_REFERENCE) != 0) { + s += "|reference"; + } + if ((mType & TYPE_STRING) != 0) { + s += "|string"; + } + if ((mType & TYPE_INT) != 0) { + s += "|integer"; + } + if ((mType & TYPE_BOOL) != 0) { + s += "|boolean"; + } + if ((mType & TYPE_COLOR) != 0) { + s += "|color"; + } + if ((mType & TYPE_FLOAT) != 0) { + s += "|float"; + } + if ((mType & TYPE_DIMEN) != 0) { + s += "|dimension"; + } + if ((mType & TYPE_FRACTION) != 0) { + s += "|fraction"; + } + if (s.isEmpty()) { + return null; + } + return s.substring(1); + } - private final int mType; - private final Integer mMin; - private final Integer mMax; - private final Boolean mL10n; + private final int mType; + private final Integer mMin; + private final Integer mMax; + private final Boolean mL10n; - public static final int BAG_KEY_ATTR_TYPE = 0x01000000; - private static final int BAG_KEY_ATTR_MIN = 0x01000001; - private static final int BAG_KEY_ATTR_MAX = 0x01000002; - private static final int BAG_KEY_ATTR_L10N = 0x01000003; + public static final int BAG_KEY_ATTR_TYPE = 0x01000000; + private static final int BAG_KEY_ATTR_MIN = 0x01000001; + private static final int BAG_KEY_ATTR_MAX = 0x01000002; + private static final int BAG_KEY_ATTR_L10N = 0x01000003; - private final static int TYPE_REFERENCE = 0x01; - private final static int TYPE_STRING = 0x02; - private final static int TYPE_INT = 0x04; - private final static int TYPE_BOOL = 0x08; - private final static int TYPE_COLOR = 0x10; - private final static int TYPE_FLOAT = 0x20; - private final static int TYPE_DIMEN = 0x40; - private final static int TYPE_FRACTION = 0x80; - private final static int TYPE_ANY_STRING = 0xee; + private final static int TYPE_REFERENCE = 0x01; + private final static int TYPE_STRING = 0x02; + private final static int TYPE_INT = 0x04; + private final static int TYPE_BOOL = 0x08; + private final static int TYPE_COLOR = 0x10; + private final static int TYPE_FLOAT = 0x20; + private final static int TYPE_DIMEN = 0x40; + private final static int TYPE_FRACTION = 0x80; + private final static int TYPE_ANY_STRING = 0xee; - private static final int TYPE_ENUM = 0x00010000; - private static final int TYPE_FLAGS = 0x00020000; + private static final int TYPE_ENUM = 0x00010000; + private static final int TYPE_FLAGS = 0x00020000; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java index 0e01624c..fa280690 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBagValue.java @@ -27,39 +27,39 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResBagValue extends ResValue implements ResValuesXmlSerializable { - protected final ResReferenceValue mParent; + protected final ResReferenceValue mParent; - public ResBagValue(ResReferenceValue parent) { - this.mParent = parent; - } + public ResBagValue(ResReferenceValue parent) { + this.mParent = parent; + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException { - String type = res.getResSpec().getType().getName(); - if ("style".equals(type)) { - new ResStyleValue(mParent, new Duo[0], null) - .serializeToResValuesXml(serializer, res); - return; - } - if ("array".equals(type)) { - new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml( - serializer, res); - return; - } - if ("plurals".equals(type)) { - new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml( - serializer, res); - return; - } + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + String type = res.getResSpec().getType().getName(); + if ("style".equals(type)) { + new ResStyleValue(mParent, new Duo[0], null) + .serializeToResValuesXml(serializer, res); + return; + } + if ("array".equals(type)) { + new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml( + serializer, res); + return; + } + if ("plurals".equals(type)) { + new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml( + serializer, res); + return; + } - serializer.startTag(null, "item"); - serializer.attribute(null, "type", type); - serializer.attribute(null, "name", res.getResSpec().getName()); - serializer.endTag(null, "item"); - } + serializer.startTag(null, "item"); + serializer.attribute(null, "type", type); + serializer.attribute(null, "name", res.getResSpec().getName()); + serializer.endTag(null, "item"); + } - public ResReferenceValue getParent() { - return mParent; - } + public ResReferenceValue getParent() { + return mParent; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java index c8334eeb..724a59c2 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResBoolValue.java @@ -20,19 +20,19 @@ package brut.androlib.res.data.value; * @author Ryszard Wiśniewski */ public class ResBoolValue extends ResScalarValue { - private final boolean mValue; + private final boolean mValue; - public ResBoolValue(boolean value, String rawValue) { - super("bool", rawValue); - this.mValue = value; - } + public ResBoolValue(boolean value, String rawValue) { + super("bool", rawValue); + this.mValue = value; + } - public boolean getValue() { - return mValue; - } + public boolean getValue() { + return mValue; + } - @Override - protected String encodeAsResXml() { - return mValue ? "true" : "false"; - } + @Override + protected String encodeAsResXml() { + return mValue ? "true" : "false"; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java index fc83da95..224186f3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResColorValue.java @@ -20,12 +20,12 @@ package brut.androlib.res.data.value; * @author Ryszard Wiśniewski */ public class ResColorValue extends ResIntValue { - public ResColorValue(int value, String rawValue) { - super(value, rawValue, "color"); - } + public ResColorValue(int value, String rawValue) { + super(value, rawValue, "color"); + } - @Override - protected String encodeAsResXml() { - return String.format("#%08x", mValue); - } + @Override + protected String encodeAsResXml() { + return String.format("#%08x", mValue); + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java index 24959a44..d00a6077 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResDimenValue.java @@ -23,12 +23,12 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class ResDimenValue extends ResIntValue { - public ResDimenValue(int value, String rawValue) { - super(value, rawValue, "dimen"); - } + public ResDimenValue(int value, String rawValue) { + super(value, rawValue, "dimen"); + } - @Override - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue); - } + @Override + protected String encodeAsResXml() throws AndrolibException { + return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java index 1dd5410b..5dba59ed 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResEnumAttr.java @@ -28,55 +28,55 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResEnumAttr extends ResAttr { - ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, - Boolean l10n, Duo[] items) { - super(parent, type, min, max, l10n); - mItems = items; - } + ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, + Boolean l10n, Duo[] items) { + super(parent, type, min, max, l10n); + mItems = items; + } - @Override - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - if (value instanceof ResIntValue) { - String ret = decodeValue(((ResIntValue) value).getValue()); - if (ret != null) { - return ret; - } - } - return super.convertToResXmlFormat(value); - } + @Override + public String convertToResXmlFormat(ResScalarValue value) + throws AndrolibException { + if (value instanceof ResIntValue) { + String ret = decodeValue(((ResIntValue) value).getValue()); + if (ret != null) { + return ret; + } + } + return super.convertToResXmlFormat(value); + } - @Override - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { - for (Duo duo : mItems) { - int intVal = duo.m2.getValue(); + @Override + protected void serializeBody(XmlSerializer serializer, ResResource res) + throws AndrolibException, IOException { + for (Duo duo : mItems) { + int intVal = duo.m2.getValue(); - serializer.startTag(null, "enum"); - serializer.attribute(null, "name", duo.m1.getReferent().getName()); - serializer.attribute(null, "value", String.valueOf(intVal)); - serializer.endTag(null, "enum"); - } - } + serializer.startTag(null, "enum"); + serializer.attribute(null, "name", duo.m1.getReferent().getName()); + serializer.attribute(null, "value", String.valueOf(intVal)); + serializer.endTag(null, "enum"); + } + } - private String decodeValue(int value) throws AndrolibException { - String value2 = mItemsCache.get(value); - if (value2 == null) { - ResReferenceValue ref = null; - for (Duo duo : mItems) { - if (duo.m2.getValue() == value) { - ref = duo.m1; - break; - } - } - if (ref != null) { - value2 = ref.getReferent().getName(); - mItemsCache.put(value, value2); - } - } - return value2; - } + private String decodeValue(int value) throws AndrolibException { + String value2 = mItemsCache.get(value); + if (value2 == null) { + ResReferenceValue ref = null; + for (Duo duo : mItems) { + if (duo.m2.getValue() == value) { + ref = duo.m1; + break; + } + } + if (ref != null) { + value2 = ref.getReferent().getName(); + mItemsCache.put(value, value2); + } + } + return value2; + } - private final Duo[] mItems; - private final Map mItemsCache = new HashMap(); + private final Duo[] mItems; + private final Map mItemsCache = new HashMap(); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java index 21656e29..d09a19e7 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFileValue.java @@ -22,21 +22,21 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class ResFileValue extends ResValue { - private final String mPath; + private final String mPath; - public ResFileValue(String path) { - this.mPath = path; - } + public ResFileValue(String path) { + this.mPath = path; + } - public String getPath() { - return mPath; - } + public String getPath() { + return mPath; + } - public String getStrippedPath() throws AndrolibException { - if (!mPath.startsWith("res/")) { - throw new AndrolibException( - "File path does not start with \"res/\": " + mPath); - } - return mPath.substring(4); - } + public String getStrippedPath() throws AndrolibException { + if (!mPath.startsWith("res/")) { + throw new AndrolibException( + "File path does not start with \"res/\": " + mPath); + } + return mPath.substring(4); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java index f73b85b9..a2052424 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFlagsAttr.java @@ -28,133 +28,133 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResFlagsAttr extends ResAttr { - ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, - Boolean l10n, Duo[] items) { - super(parent, type, min, max, l10n); + ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, + Boolean l10n, Duo[] items) { + super(parent, type, min, max, l10n); - mItems = new FlagItem[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue()); - } - } + mItems = new FlagItem[items.length]; + for (int i = 0; i < items.length; i++) { + mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue()); + } + } - @Override - public String convertToResXmlFormat(ResScalarValue value) - throws AndrolibException { - if (!(value instanceof ResIntValue)) { - return super.convertToResXmlFormat(value); - } - loadFlags(); - int intVal = ((ResIntValue) value).getValue(); + @Override + public String convertToResXmlFormat(ResScalarValue value) + throws AndrolibException { + if (!(value instanceof ResIntValue)) { + return super.convertToResXmlFormat(value); + } + loadFlags(); + int intVal = ((ResIntValue) value).getValue(); - if (intVal == 0) { - return renderFlags(mZeroFlags); - } + if (intVal == 0) { + return renderFlags(mZeroFlags); + } - FlagItem[] flagItems = new FlagItem[mFlags.length]; - int[] flags = new int[mFlags.length]; - int flagsCount = 0; - for (int i = 0; i < mFlags.length; i++) { - FlagItem flagItem = mFlags[i]; - int flag = flagItem.flag; + FlagItem[] flagItems = new FlagItem[mFlags.length]; + int[] flags = new int[mFlags.length]; + int flagsCount = 0; + for (int i = 0; i < mFlags.length; i++) { + FlagItem flagItem = mFlags[i]; + int flag = flagItem.flag; - if ((intVal & flag) != flag) { - continue; - } + if ((intVal & flag) != flag) { + continue; + } - if (!isSubpartOf(flag, flags)) { - flags[flagsCount] = flag; - flagItems[flagsCount++] = flagItem; - } - } - return renderFlags(Arrays.copyOf(flagItems, flagsCount)); - } + if (!isSubpartOf(flag, flags)) { + flags[flagsCount] = flag; + flagItems[flagsCount++] = flagItem; + } + } + return renderFlags(Arrays.copyOf(flagItems, flagsCount)); + } - @Override - protected void serializeBody(XmlSerializer serializer, ResResource res) - throws AndrolibException, IOException { - for (int i = 0; i < mItems.length; i++) { - FlagItem item = mItems[i]; + @Override + protected void serializeBody(XmlSerializer serializer, ResResource res) + throws AndrolibException, IOException { + for (int i = 0; i < mItems.length; i++) { + FlagItem item = mItems[i]; - serializer.startTag(null, "flag"); - serializer.attribute(null, "name", item.getValue()); - serializer.attribute(null, "value", - String.format("0x%08x", item.flag)); - serializer.endTag(null, "flag"); - } - } + serializer.startTag(null, "flag"); + serializer.attribute(null, "name", item.getValue()); + serializer.attribute(null, "value", + String.format("0x%08x", item.flag)); + serializer.endTag(null, "flag"); + } + } - private boolean isSubpartOf(int flag, int[] flags) { - for (int i = 0; i < flags.length; i++) { - if ((flags[i] & flag) == flag) { - return true; - } - } - return false; - } + private boolean isSubpartOf(int flag, int[] flags) { + for (int i = 0; i < flags.length; i++) { + if ((flags[i] & flag) == flag) { + return true; + } + } + return false; + } - private String renderFlags(FlagItem[] flags) throws AndrolibException { - String ret = ""; - for (int i = 0; i < flags.length; i++) { - ret += "|" + flags[i].getValue(); - } - if (ret.isEmpty()) { - return ret; - } - return ret.substring(1); - } + private String renderFlags(FlagItem[] flags) throws AndrolibException { + String ret = ""; + for (int i = 0; i < flags.length; i++) { + ret += "|" + flags[i].getValue(); + } + if (ret.isEmpty()) { + return ret; + } + return ret.substring(1); + } - private void loadFlags() { - if (mFlags != null) { - return; - } + private void loadFlags() { + if (mFlags != null) { + return; + } - FlagItem[] zeroFlags = new FlagItem[mItems.length]; - int zeroFlagsCount = 0; - FlagItem[] flags = new FlagItem[mItems.length]; - int flagsCount = 0; + FlagItem[] zeroFlags = new FlagItem[mItems.length]; + int zeroFlagsCount = 0; + FlagItem[] flags = new FlagItem[mItems.length]; + int flagsCount = 0; - for (int i = 0; i < mItems.length; i++) { - FlagItem item = mItems[i]; - if (item.flag == 0) { - zeroFlags[zeroFlagsCount++] = item; - } else { - flags[flagsCount++] = item; - } - } + for (int i = 0; i < mItems.length; i++) { + FlagItem item = mItems[i]; + if (item.flag == 0) { + zeroFlags[zeroFlagsCount++] = item; + } else { + flags[flagsCount++] = item; + } + } - mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount); - mFlags = Arrays.copyOf(flags, flagsCount); + mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount); + mFlags = Arrays.copyOf(flags, flagsCount); - Arrays.sort(mFlags, new Comparator() { - @Override - public int compare(FlagItem o1, FlagItem o2) { - return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo( - Integer.bitCount(o1.flag)); - } - }); - } + Arrays.sort(mFlags, new Comparator() { + @Override + public int compare(FlagItem o1, FlagItem o2) { + return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo( + Integer.bitCount(o1.flag)); + } + }); + } - private final FlagItem[] mItems; + private final FlagItem[] mItems; - private FlagItem[] mZeroFlags; - private FlagItem[] mFlags; + private FlagItem[] mZeroFlags; + private FlagItem[] mFlags; - private static class FlagItem { - public final ResReferenceValue ref; - public final int flag; - public String value; + private static class FlagItem { + public final ResReferenceValue ref; + public final int flag; + public String value; - public FlagItem(ResReferenceValue ref, int flag) { - this.ref = ref; - this.flag = flag; - } + public FlagItem(ResReferenceValue ref, int flag) { + this.ref = ref; + this.flag = flag; + } - public String getValue() throws AndrolibException { - if (value == null) { - value = ref.getReferent().getName(); - } - return value; - } - } + public String getValue() throws AndrolibException { + if (value == null) { + value = ref.getReferent().getName(); + } + return value; + } + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java index c7680e6e..f50b3d7c 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFloatValue.java @@ -20,19 +20,19 @@ package brut.androlib.res.data.value; * @author Ryszard Wiśniewski */ public class ResFloatValue extends ResScalarValue { - private final float mValue; + private final float mValue; - public ResFloatValue(float value, String rawValue) { - super("float", rawValue); - this.mValue = value; - } + public ResFloatValue(float value, String rawValue) { + super("float", rawValue); + this.mValue = value; + } - public float getValue() { - return mValue; - } + public float getValue() { + return mValue; + } - @Override - protected String encodeAsResXml() { - return String.valueOf(mValue); - } + @Override + protected String encodeAsResXml() { + return String.valueOf(mValue); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java index 3fddbfad..c3325834 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResFractionValue.java @@ -23,12 +23,12 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class ResFractionValue extends ResIntValue { - public ResFractionValue(int value, String rawValue) { - super(value, rawValue, "fraction"); - } + public ResFractionValue(int value, String rawValue) { + super(value, rawValue, "fraction"); + } - @Override - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue); - } + @Override + protected String encodeAsResXml() throws AndrolibException { + return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java index 38eaaed9..9f1dd33f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIdValue.java @@ -26,13 +26,13 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResIdValue extends ResValue implements ResValuesXmlSerializable { - @Override - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException { - serializer.startTag(null, "item"); - serializer - .attribute(null, "type", res.getResSpec().getType().getName()); - serializer.attribute(null, "name", res.getResSpec().getName()); - serializer.endTag(null, "item"); - } + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + serializer.startTag(null, "item"); + serializer + .attribute(null, "type", res.getResSpec().getType().getName()); + serializer.attribute(null, "name", res.getResSpec().getName()); + serializer.endTag(null, "item"); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java index d1662657..7193d196 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResIntValue.java @@ -23,25 +23,25 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public class ResIntValue extends ResScalarValue { - protected final int mValue; - private int type; + protected final int mValue; + private int type; - public ResIntValue(int value, String rawValue, int type) { - this(value, rawValue, "integer"); - this.type = type; - } + public ResIntValue(int value, String rawValue, int type) { + this(value, rawValue, "integer"); + this.type = type; + } - public ResIntValue(int value, String rawValue, String type) { - super(type, rawValue); - this.mValue = value; - } + public ResIntValue(int value, String rawValue, String type) { + super(type, rawValue); + this.mValue = value; + } - public int getValue() { - return mValue; - } + public int getValue() { + return mValue; + } - @Override - protected String encodeAsResXml() throws AndrolibException { - return TypedValue.coerceToString(type, mValue); - } + @Override + protected String encodeAsResXml() throws AndrolibException { + return TypedValue.coerceToString(type, mValue); + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java index 40b89a05..1f5eaad0 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResPluralsValue.java @@ -29,56 +29,42 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResPluralsValue extends ResBagValue implements - ResValuesXmlSerializable { - ResPluralsValue(ResReferenceValue parent, - Duo[] items) { - super(parent); + ResValuesXmlSerializable { + ResPluralsValue(ResReferenceValue parent, + Duo[] items) { + super(parent); - mItems = new ResScalarValue[6]; - for (int i = 0; i < items.length; i++) { - mItems[items[i].m1 - BAG_KEY_PLURALS_START] = items[i].m2; - } - } + mItems = new ResScalarValue[6]; + for (int i = 0; i < items.length; i++) { + mItems[items[i].m1 - BAG_KEY_PLURALS_START] = items[i].m2; + } + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException { - serializer.startTag(null, "plurals"); - serializer.attribute(null, "name", res.getResSpec().getName()); - for (int i = 0; i < mItems.length; i++) { - ResScalarValue item = mItems[i]; - if (item == null) { - continue; - } + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + serializer.startTag(null, "plurals"); + serializer.attribute(null, "name", res.getResSpec().getName()); + for (int i = 0; i < mItems.length; i++) { + ResScalarValue item = mItems[i]; + if (item == null) { + continue; + } - ResScalarValue rawValue = item; + ResScalarValue rawValue = item; - serializer.startTag(null, "item"); - serializer.attribute(null, "quantity", QUANTITY_MAP[i]); - if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue - .encodeAsResXmlValue())) { - serializer.text(item.encodeAsResXmlValueExt()); - } else { - String recode = item.encodeAsResXmlValue(); - // Dirty, but working fix @miuirussia - for (int j = 0; j < 10; j++) { - recode = StringUtils.replace( - recode, - "%" + Integer.toString(j) + "$" - + Integer.toString(j) + "$", - "%" + Integer.toString(j) + "$"); - } - serializer.text(recode); - } - serializer.endTag(null, "item"); - } - serializer.endTag(null, "plurals"); - } + serializer.startTag(null, "item"); + serializer.attribute(null, "quantity", QUANTITY_MAP[i]); + serializer.text(ResXmlEncoders.enumerateNonPositionalSubstitutionsIfRequired(item.encodeAsResXmlValue())); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "plurals"); + } - private final ResScalarValue[] mItems; + private final ResScalarValue[] mItems; - public static final int BAG_KEY_PLURALS_START = 0x01000004; - public static final int BAG_KEY_PLURALS_END = 0x01000009; - private static final String[] QUANTITY_MAP = new String[] { "other", - "zero", "one", "two", "few", "many" }; + public static final int BAG_KEY_PLURALS_START = 0x01000004; + public static final int BAG_KEY_PLURALS_END = 0x01000009; + private static final String[] QUANTITY_MAP = new String[] { "other", + "zero", "one", "two", "few", "many" }; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java index 96398768..1d037610 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResReferenceValue.java @@ -24,44 +24,43 @@ import brut.androlib.res.data.ResResSpec; * @author Ryszard Wiśniewski */ public class ResReferenceValue extends ResIntValue { - private final ResPackage mPackage; - private final boolean mTheme; + private final ResPackage mPackage; + private final boolean mTheme; - public ResReferenceValue(ResPackage package_, int value, String rawValue) { - this(package_, value, rawValue, false); - } + public ResReferenceValue(ResPackage package_, int value, String rawValue) { + this(package_, value, rawValue, false); + } - public ResReferenceValue(ResPackage package_, int value, String rawValue, - boolean theme) { - super(value, rawValue, "reference"); - mPackage = package_; - mTheme = theme; - } + public ResReferenceValue(ResPackage package_, int value, String rawValue, + boolean theme) { + super(value, rawValue, "reference"); + mPackage = package_; + mTheme = theme; + } - @Override - protected String encodeAsResXml() throws AndrolibException { - if (isNull()) { - return "@null"; - } + @Override + protected String encodeAsResXml() throws AndrolibException { + if (isNull()) { + return "@null"; + } - ResResSpec spec = getReferent(); - boolean newId = spec.hasDefaultResource() - && spec.getDefaultResource().getValue() instanceof ResIdValue; + ResResSpec spec = getReferent(); + boolean newId = spec.hasDefaultResource() + && spec.getDefaultResource().getValue() instanceof ResIdValue; - // generate the beginning to fix @android - String mStart = (mTheme ? '?' : '@') + (newId ? "+" : ""); - // mStart = mStart.replace("@android", "@*android"); + // generate the beginning to fix @android + String mStart = (mTheme ? '?' : '@') + (newId ? "+" : ""); - return mStart - + spec.getFullName(mPackage, mTheme - && spec.getType().getName().equals("attr")); - } + return mStart + + spec.getFullName(mPackage, mTheme + && spec.getType().getName().equals("attr")); + } - public ResResSpec getReferent() throws AndrolibException { - return mPackage.getResTable().getResSpec(getValue()); - } + public ResResSpec getReferent() throws AndrolibException { + return mPackage.getResTable().getResSpec(getValue()); + } - public boolean isNull() { - return mValue == 0; - } + public boolean isNull() { + return mValue == 0; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java index c540eee8..ff8c3f18 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java @@ -28,106 +28,81 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public abstract class ResScalarValue extends ResValue implements - ResXmlEncodable, ResValuesXmlSerializable { - protected final String mType; - protected final String mRawValue; + ResXmlEncodable, ResValuesXmlSerializable { + protected final String mType; + protected final String mRawValue; - protected ResScalarValue(String type, String rawValue) { - mType = type; - mRawValue = rawValue; - } + protected ResScalarValue(String type, String rawValue) { + mType = type; + mRawValue = rawValue; + } - @Override - public String encodeAsResXmlAttr() throws AndrolibException { - if (mRawValue != null) { - return mRawValue; - } - return encodeAsResXml().replace("@android:", "@*android:"); - } + @Override + public String encodeAsResXmlAttr() throws AndrolibException { + if (mRawValue != null) { + return mRawValue; + } + return encodeAsResXml(); + } - public String encodeAsResXmlItemValue() throws AndrolibException { - return encodeAsResXmlValue().replace("@android:", "@*android:"); - } + public String encodeAsResXmlItemValue() throws AndrolibException { + return encodeAsResXmlValue(); + } - @Override - public String encodeAsResXmlValue() throws AndrolibException { - if (mRawValue != null) { - return mRawValue; - } - return encodeAsResXmlValueExt().replace("@android:", "@*android:"); - } + @Override + public String encodeAsResXmlValue() throws AndrolibException { + if (mRawValue != null) { + return mRawValue; + } + return encodeAsResXml(); + } - public String encodeAsResXmlValueExt() throws AndrolibException { - String rawValue = mRawValue; - if (rawValue != null) { - if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) { - int count = 1; - StringBuilder result = new StringBuilder(); - String tmp1[] = rawValue.split("%%", -1); - int tmp1_sz = tmp1.length; - for (int i = 0; i < tmp1_sz; i++) { - String cur1 = tmp1[i]; - String tmp2[] = cur1.split("%", -1); - int tmp2_sz = tmp2.length; - for (int j = 0; j < tmp2_sz; j++) { - String cur2 = tmp2[j]; - result.append(cur2); - if (j != (tmp2_sz - 1)) { - result.append('%').append(count).append('$'); - count++; - } - } - if (i != (tmp1_sz - 1)) { - result.append("%%"); - } - } - rawValue = result.toString(); - } - return rawValue; - } - return encodeAsResXml(); - } + public String encodeAsResXmlNonEscapedItemValue() throws AndrolibException { + return encodeAsResXmlValue().replace("&", "&").replace("<","<"); + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException { - String type = res.getResSpec().getType().getName(); - boolean item = !"reference".equals(mType) && !type.equals(mType); + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + String type = res.getResSpec().getType().getName(); + boolean item = !"reference".equals(mType) && !type.equals(mType); - String body = encodeAsResXmlValue(); + String body = encodeAsResXmlValue(); - // check for resource reference - if (body.contains("@")) { - if (!res.getFilePath().contains("string")) { - item = true; - } - } + // check for resource reference + if (!type.equalsIgnoreCase("color")) { + if (body.contains("@")) { + if (!res.getFilePath().contains("string")) { + item = true; + } + } + } - // check for using attrib as node or item - String tagName = item ? "item" : type; + // check for using attrib as node or item + String tagName = item ? "item" : type; - serializer.startTag(null, tagName); - if (item) { - serializer.attribute(null, "type", type); - } - serializer.attribute(null, "name", res.getResSpec().getName()); + serializer.startTag(null, tagName); + if (item) { + serializer.attribute(null, "type", type); + } + serializer.attribute(null, "name", res.getResSpec().getName()); - serializeExtraXmlAttrs(serializer, res); + serializeExtraXmlAttrs(serializer, res); - if (!body.isEmpty()) { - serializer.ignorableWhitespace(body); - } + if (!body.isEmpty()) { + serializer.ignorableWhitespace(body); + } - serializer.endTag(null, tagName); - } + serializer.endTag(null, tagName); + } - public String getType() { - return mType; - } + public String getType() { + return mType; + } - protected void serializeExtraXmlAttrs(XmlSerializer serializer, - ResResource res) throws IOException { - } + protected void serializeExtraXmlAttrs(XmlSerializer serializer, + ResResource res) throws IOException { + } - protected abstract String encodeAsResXml() throws AndrolibException; + protected abstract String encodeAsResXml() throws AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java index 796aeb59..c248cc5d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStringValue.java @@ -27,41 +27,41 @@ import org.xmlpull.v1.XmlSerializer; */ public class ResStringValue extends ResScalarValue { - public ResStringValue(String value) { - this(value, "string"); - } + public ResStringValue(String value) { + this(value, "string"); + } - public ResStringValue(String value, String type) { - super(type, value); - } + public ResStringValue(String value, String type) { + super(type, value); + } - @Override - public String encodeAsResXmlAttr() { - return ResXmlEncoders.encodeAsResXmlAttr(mRawValue); - } + @Override + public String encodeAsResXmlAttr() { + return ResXmlEncoders.encodeAsResXmlAttr(mRawValue); + } - @Override - public String encodeAsResXmlItemValue() { - return ResXmlEncoders - .enumerateNonPositionalSubstitutions(ResXmlEncoders - .encodeAsXmlValue(mRawValue)); - } + @Override + public String encodeAsResXmlItemValue() { + return ResXmlEncoders + .enumerateNonPositionalSubstitutionsIfRequired(ResXmlEncoders + .encodeAsXmlValue(mRawValue)); + } - @Override - public String encodeAsResXmlValue() { - return ResXmlEncoders.encodeAsXmlValue(mRawValue); - } + @Override + public String encodeAsResXmlValue() { + return ResXmlEncoders.encodeAsXmlValue(mRawValue); + } - @Override - protected String encodeAsResXml() throws AndrolibException { - throw new UnsupportedOperationException(); - } + @Override + protected String encodeAsResXml() throws AndrolibException { + throw new UnsupportedOperationException(); + } - @Override - protected void serializeExtraXmlAttrs(XmlSerializer serializer, - ResResource res) throws IOException { - if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) { - serializer.attribute(null, "formatted", "false"); - } - } + @Override + protected void serializeExtraXmlAttrs(XmlSerializer serializer, + ResResource res) throws IOException { + if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) { + serializer.attribute(null, "formatted", "false"); + } + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java index 4fcbbdcf..35c38c5e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResStyleValue.java @@ -28,53 +28,55 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public class ResStyleValue extends ResBagValue implements - ResValuesXmlSerializable { - ResStyleValue(ResReferenceValue parent, - Duo[] items, ResValueFactory factory) { - super(parent); + ResValuesXmlSerializable { + ResStyleValue(ResReferenceValue parent, + Duo[] items, ResValueFactory factory) { + super(parent); - mItems = new Duo[items.length]; - for (int i = 0; i < items.length; i++) { - mItems[i] = new Duo( - factory.newReference(items[i].m1, null), items[i].m2); - } - } + mItems = new Duo[items.length]; + for (int i = 0; i < items.length; i++) { + mItems[i] = new Duo( + factory.newReference(items[i].m1, null), items[i].m2); + } + } - @Override - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException { - serializer.startTag(null, "style"); - serializer.attribute(null, "name", res.getResSpec().getName()); - if (!mParent.isNull()) { - serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr()); - } - for (int i = 0; i < mItems.length; i++) { - ResResSpec spec = mItems[i].m1.getReferent(); + @Override + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException { + serializer.startTag(null, "style"); + serializer.attribute(null, "name", res.getResSpec().getName()); + if (!mParent.isNull()) { + serializer.attribute(null, "parent", mParent.encodeAsResXmlAttr()); + } else if (res.getResSpec().getName().indexOf('.') != -1) { + serializer.attribute(null, "parent", ""); + } + for (int i = 0; i < mItems.length; i++) { + ResResSpec spec = mItems[i].m1.getReferent(); - // hacky-fix remove bad ReferenceVars - if (spec.getDefaultResource().getValue().toString() - .contains("ResReferenceValue@")) { - continue; - } - ResAttr attr = (ResAttr) spec.getDefaultResource().getValue(); - String value = attr.convertToResXmlFormat(mItems[i].m2); + // hacky-fix remove bad ReferenceVars + if (spec.getDefaultResource().getValue().toString() + .contains("ResReferenceValue@")) { + continue; + } + ResAttr attr = (ResAttr) spec.getDefaultResource().getValue(); + String value = attr.convertToResXmlFormat(mItems[i].m2); - if (value == null) { - value = mItems[i].m2.encodeAsResXmlValue(); - } + if (value == null) { + value = mItems[i].m2.encodeAsResXmlValue(); + } - if (value == null) { - continue; - } + if (value == null) { + continue; + } - serializer.startTag(null, "item"); - serializer.attribute(null, "name", - spec.getFullName(res.getResSpec().getPackage(), true)); - serializer.text(value); - serializer.endTag(null, "item"); - } - serializer.endTag(null, "style"); - } + serializer.startTag(null, "item"); + serializer.attribute(null, "name", + spec.getFullName(res.getResSpec().getPackage(), true)); + serializer.text(value); + serializer.endTag(null, "item"); + } + serializer.endTag(null, "style"); + } - private final Duo[] mItems; + private final Duo[] mItems; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java index 3c4693f6..0331112d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java @@ -25,77 +25,77 @@ import brut.util.Duo; * @author Ryszard Wiśniewski */ public class ResValueFactory { - private final ResPackage mPackage; + private final ResPackage mPackage; - public ResValueFactory(ResPackage pakage_) { - this.mPackage = pakage_; - } + public ResValueFactory(ResPackage pakage_) { + this.mPackage = pakage_; + } - public ResScalarValue factory(int type, int value, String rawValue) - throws AndrolibException { - switch (type) { - case TypedValue.TYPE_REFERENCE: - return newReference(value, rawValue); - case TypedValue.TYPE_ATTRIBUTE: - return newReference(value, rawValue, true); - case TypedValue.TYPE_STRING: - return new ResStringValue(rawValue); - case TypedValue.TYPE_FLOAT: - return new ResFloatValue(Float.intBitsToFloat(value), rawValue); - case TypedValue.TYPE_DIMENSION: - return new ResDimenValue(value, rawValue); - case TypedValue.TYPE_FRACTION: - return new ResFractionValue(value, rawValue); - case TypedValue.TYPE_INT_BOOLEAN: - return new ResBoolValue(value != 0, rawValue); - } + public ResScalarValue factory(int type, int value, String rawValue) + throws AndrolibException { + switch (type) { + case TypedValue.TYPE_REFERENCE: + return newReference(value, rawValue); + case TypedValue.TYPE_ATTRIBUTE: + return newReference(value, rawValue, true); + case TypedValue.TYPE_STRING: + return new ResStringValue(rawValue); + case TypedValue.TYPE_FLOAT: + return new ResFloatValue(Float.intBitsToFloat(value), rawValue); + case TypedValue.TYPE_DIMENSION: + return new ResDimenValue(value, rawValue); + case TypedValue.TYPE_FRACTION: + return new ResFractionValue(value, rawValue); + case TypedValue.TYPE_INT_BOOLEAN: + return new ResBoolValue(value != 0, rawValue); + } - if (type >= TypedValue.TYPE_FIRST_COLOR_INT - && type <= TypedValue.TYPE_LAST_COLOR_INT) { - return new ResColorValue(value, rawValue); - } - if (type >= TypedValue.TYPE_FIRST_INT - && type <= TypedValue.TYPE_LAST_INT) { - return new ResIntValue(value, rawValue, type); - } + if (type >= TypedValue.TYPE_FIRST_COLOR_INT + && type <= TypedValue.TYPE_LAST_COLOR_INT) { + return new ResColorValue(value, rawValue); + } + if (type >= TypedValue.TYPE_FIRST_INT + && type <= TypedValue.TYPE_LAST_INT) { + return new ResIntValue(value, rawValue, type); + } - throw new AndrolibException("Invalid value type: " + type); - } + throw new AndrolibException("Invalid value type: " + type); + } - public ResValue factory(String value) { - if (value.startsWith("res/")) { - return new ResFileValue(value); - } - return new ResStringValue(value); - } + public ResValue factory(String value) { + if (value.startsWith("res/")) { + return new ResFileValue(value); + } + return new ResStringValue(value); + } - public ResBagValue bagFactory(int parent, - Duo[] items) throws AndrolibException { - ResReferenceValue parentVal = newReference(parent, null); + public ResBagValue bagFactory(int parent, + Duo[] items) throws AndrolibException { + ResReferenceValue parentVal = newReference(parent, null); - if (items.length == 0) { - return new ResBagValue(parentVal); - } - int key = items[0].m1; - if (key == ResAttr.BAG_KEY_ATTR_TYPE) { - return ResAttr.factory(parentVal, items, this, mPackage); - } - if (key == ResArrayValue.BAG_KEY_ARRAY_START) { - return new ResArrayValue(parentVal, items); - } - if (key >= ResPluralsValue.BAG_KEY_PLURALS_START - && key <= ResPluralsValue.BAG_KEY_PLURALS_END) { - return new ResPluralsValue(parentVal, items); - } - return new ResStyleValue(parentVal, items, this); - } + if (items.length == 0) { + return new ResBagValue(parentVal); + } + int key = items[0].m1; + if (key == ResAttr.BAG_KEY_ATTR_TYPE) { + return ResAttr.factory(parentVal, items, this, mPackage); + } + if (key == ResArrayValue.BAG_KEY_ARRAY_START) { + return new ResArrayValue(parentVal, items); + } + if (key >= ResPluralsValue.BAG_KEY_PLURALS_START + && key <= ResPluralsValue.BAG_KEY_PLURALS_END) { + return new ResPluralsValue(parentVal, items); + } + return new ResStyleValue(parentVal, items, this); + } - public ResReferenceValue newReference(int resID, String rawValue) { - return newReference(resID, rawValue, false); - } + public ResReferenceValue newReference(int resID, String rawValue) { + return newReference(resID, rawValue, false); + } - public ResReferenceValue newReference(int resID, String rawValue, - boolean theme) { - return new ResReferenceValue(mPackage, resID, rawValue, theme); - } + public ResReferenceValue newReference(int resID, String rawValue, + boolean theme) { + return new ResReferenceValue(mPackage, resID, rawValue, theme); + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java index 35a6ea48..2c619984 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java @@ -34,410 +34,426 @@ import org.apache.commons.io.input.CountingInputStream; * @author Ryszard Wiśniewski */ public class ARSCDecoder { - public static ARSCData decode(InputStream arscStream, - boolean findFlagsOffsets, boolean keepBroken) - throws AndrolibException { - return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable()); - } + public static ARSCData decode(InputStream arscStream, + boolean findFlagsOffsets, boolean keepBroken) + throws AndrolibException { + return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable()); + } - public static ARSCData decode(InputStream arscStream, - boolean findFlagsOffsets, boolean keepBroken, ResTable resTable) - throws AndrolibException { - try { - ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, - findFlagsOffsets, keepBroken); - ResPackage[] pkgs = decoder.readTable(); - return new ARSCData(pkgs, decoder.mFlagsOffsets == null ? null - : decoder.mFlagsOffsets.toArray(new FlagsOffset[0]), - resTable); - } catch (IOException ex) { - throw new AndrolibException("Could not decode arsc file", ex); - } - } + public static ARSCData decode(InputStream arscStream, + boolean findFlagsOffsets, boolean keepBroken, ResTable resTable) + throws AndrolibException { + try { + ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable, + findFlagsOffsets, keepBroken); + ResPackage[] pkgs = decoder.readTable(); + return new ARSCData(pkgs, decoder.mFlagsOffsets == null ? null + : decoder.mFlagsOffsets.toArray(new FlagsOffset[0]), + resTable); + } catch (IOException ex) { + throw new AndrolibException("Could not decode arsc file", ex); + } + } - private ARSCDecoder(InputStream arscStream, ResTable resTable, - boolean storeFlagsOffsets, boolean keepBroken) { - if (storeFlagsOffsets) { - arscStream = mCountIn = new CountingInputStream(arscStream); - mFlagsOffsets = new ArrayList(); - } else { - mCountIn = null; - mFlagsOffsets = null; - } - mIn = new ExtDataInput(new LEDataInputStream(arscStream)); - mResTable = resTable; - mKeepBroken = keepBroken; - } + private ARSCDecoder(InputStream arscStream, ResTable resTable, + boolean storeFlagsOffsets, boolean keepBroken) { + if (storeFlagsOffsets) { + arscStream = mCountIn = new CountingInputStream(arscStream); + mFlagsOffsets = new ArrayList(); + } else { + mCountIn = null; + mFlagsOffsets = null; + } + mIn = new ExtDataInput(new LEDataInputStream(arscStream)); + mResTable = resTable; + mKeepBroken = keepBroken; + } - private ResPackage[] readTable() throws IOException, AndrolibException { - nextChunkCheckType(Header.TYPE_TABLE); - int packageCount = mIn.readInt(); + private ResPackage[] readTable() throws IOException, AndrolibException { + nextChunkCheckType(Header.TYPE_TABLE); + int packageCount = mIn.readInt(); - mTableStrings = StringBlock.read(mIn); - ResPackage[] packages = new ResPackage[packageCount]; + mTableStrings = StringBlock.read(mIn); + ResPackage[] packages = new ResPackage[packageCount]; - nextChunk(); - for (int i = 0; i < packageCount; i++) { - packages[i] = readPackage(); - } + nextChunk(); + for (int i = 0; i < packageCount; i++) { + packages[i] = readPackage(); + } + return packages; + } - // store package - if (this.mResTable.isPackageInfoValueSet("cur_package") != true) { - this.mResTable.addPackageInfo("cur_package", packages[0].getName()); - } - return packages; - } - - private ResPackage readPackage() throws IOException, AndrolibException { - checkChunkType(Header.TYPE_PACKAGE); - int id = (byte) mIn.readInt(); - String name = mIn.readNulEndedString(128, true); + private ResPackage readPackage() throws IOException, AndrolibException { + checkChunkType(Header.TYPE_PACKAGE); + int id = (byte) mIn.readInt(); + String name = mIn.readNulEndedString(128, true); /* typeNameStrings */mIn.skipInt(); /* typeNameCount */mIn.skipInt(); /* specNameStrings */mIn.skipInt(); /* specNameCount */mIn.skipInt(); - mTypeNames = StringBlock.read(mIn); - mSpecNames = StringBlock.read(mIn); + mTypeNames = StringBlock.read(mIn); + mSpecNames = StringBlock.read(mIn); - mResId = id << 24; - mPkg = new ResPackage(mResTable, id, name); + mResId = id << 24; + mPkg = new ResPackage(mResTable, id, name); - nextChunk(); - while (mHeader.type == Header.TYPE_TYPE) { - readType(); - } + nextChunk(); + while (mHeader.type == Header.TYPE_TYPE) { + readType(); + } - return mPkg; - } + return mPkg; + } - private ResType readType() throws AndrolibException, IOException { - checkChunkType(Header.TYPE_TYPE); - byte id = mIn.readByte(); - mIn.skipBytes(3); - int entryCount = mIn.readInt(); + private ResType readType() throws AndrolibException, IOException { + checkChunkType(Header.TYPE_TYPE); + byte id = mIn.readByte(); + mIn.skipBytes(3); + int entryCount = mIn.readInt(); - mMissingResSpecs = new boolean[entryCount]; - Arrays.fill(mMissingResSpecs, true); + mMissingResSpecs = new boolean[entryCount]; + Arrays.fill(mMissingResSpecs, true); - if (mFlagsOffsets != null) { - mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount)); - } + if (mFlagsOffsets != null) { + mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount)); + } /* flags */mIn.skipBytes(entryCount * 4); - mResId = (0xff000000 & mResId) | id << 16; - mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg); - mPkg.addType(mType); + mResId = (0xff000000 & mResId) | id << 16; + mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg); + mPkg.addType(mType); - while (nextChunk().type == Header.TYPE_CONFIG) { - readConfig(); - } + while (nextChunk().type == Header.TYPE_CONFIG) { + readConfig(); + } - addMissingResSpecs(); + addMissingResSpecs(); - return mType; - } + return mType; + } - private ResConfig readConfig() throws IOException, AndrolibException { - checkChunkType(Header.TYPE_CONFIG); + private ResConfig readConfig() throws IOException, AndrolibException { + checkChunkType(Header.TYPE_CONFIG); /* typeId */mIn.skipInt(); - int entryCount = mIn.readInt(); + int entryCount = mIn.readInt(); /* entriesStart */mIn.skipInt(); - ResConfigFlags flags = readConfigFlags(); - int[] entryOffsets = mIn.readIntArray(entryCount); + ResConfigFlags flags = readConfigFlags(); + int[] entryOffsets = mIn.readIntArray(entryCount); - if (flags.isInvalid) { - String resName = mType.getName() + flags.getQualifiers(); - if (mKeepBroken) { - LOGGER.warning("Invalid config flags detected: " + resName); - } else { - LOGGER.warning("Invalid config flags detected. Dropping resources: " - + resName); - } - } + if (flags.isInvalid) { + String resName = mType.getName() + flags.getQualifiers(); + if (mKeepBroken) { + LOGGER.warning("Invalid config flags detected: " + resName); + } else { + LOGGER.warning("Invalid config flags detected. Dropping resources: " + + resName); + } + } - mConfig = flags.isInvalid && !mKeepBroken ? null : mPkg - .getOrCreateConfig(flags); + mConfig = flags.isInvalid && !mKeepBroken ? null : mPkg + .getOrCreateConfig(flags); - for (int i = 0; i < entryOffsets.length; i++) { - if (entryOffsets[i] != -1) { - mMissingResSpecs[i] = false; - mResId = (mResId & 0xffff0000) | i; - readEntry(); - } - } + for (int i = 0; i < entryOffsets.length; i++) { + if (entryOffsets[i] != -1) { + mMissingResSpecs[i] = false; + mResId = (mResId & 0xffff0000) | i; + readEntry(); + } + } - return mConfig; - } + return mConfig; + } - private void readEntry() throws IOException, AndrolibException { + private void readEntry() throws IOException, AndrolibException { /* size */mIn.skipBytes(2); - short flags = mIn.readShort(); - int specNamesId = mIn.readInt(); + short flags = mIn.readShort(); + int specNamesId = mIn.readInt(); - ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue() - : readComplexEntry(); + ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue() + : readComplexEntry(); - if (mConfig == null) { - return; - } + if (mConfig == null) { + return; + } - ResID resId = new ResID(mResId); - ResResSpec spec; - if (mPkg.hasResSpec(resId)) { - spec = mPkg.getResSpec(resId); - } else { - spec = new ResResSpec(resId, mSpecNames.getString(specNamesId), - mPkg, mType); - mPkg.addResSpec(spec); - mType.addResSpec(spec); - } - ResResource res = new ResResource(mConfig, spec, value); + ResID resId = new ResID(mResId); + ResResSpec spec; + if (mPkg.hasResSpec(resId)) { + spec = mPkg.getResSpec(resId); + } else { + spec = new ResResSpec(resId, mSpecNames.getString(specNamesId), + mPkg, mType); + mPkg.addResSpec(spec); + mType.addResSpec(spec); + } + ResResource res = new ResResource(mConfig, spec, value); - mConfig.addResource(res); - spec.addResource(res); - mPkg.addResource(res); - } + mConfig.addResource(res); + spec.addResource(res); + mPkg.addResource(res); + } - private ResBagValue readComplexEntry() throws IOException, - AndrolibException { - int parent = mIn.readInt(); - int count = mIn.readInt(); + private ResBagValue readComplexEntry() throws IOException, + AndrolibException { + int parent = mIn.readInt(); + int count = mIn.readInt(); - ResValueFactory factory = mPkg.getValueFactory(); - Duo[] items = new Duo[count]; - for (int i = 0; i < count; i++) { - items[i] = new Duo(mIn.readInt(), - (ResScalarValue) readValue()); - } + ResValueFactory factory = mPkg.getValueFactory(); + Duo[] items = new Duo[count]; + for (int i = 0; i < count; i++) { + items[i] = new Duo(mIn.readInt(), + (ResScalarValue) readValue()); + } - return factory.bagFactory(parent, items); - } + return factory.bagFactory(parent, items); + } - private ResValue readValue() throws IOException, AndrolibException { + private ResValue readValue() throws IOException, AndrolibException { /* size */mIn.skipCheckShort((short) 8); /* zero */mIn.skipCheckByte((byte) 0); - byte type = mIn.readByte(); - int data = mIn.readInt(); + byte type = mIn.readByte(); + int data = mIn.readInt(); - return type == TypedValue.TYPE_STRING ? mPkg.getValueFactory().factory( - mTableStrings.getHTML(data)) : mPkg.getValueFactory().factory( - type, data, null); - } + return type == TypedValue.TYPE_STRING ? mPkg.getValueFactory().factory( + mTableStrings.getHTML(data)) : mPkg.getValueFactory().factory( + type, data, null); + } - private ResConfigFlags readConfigFlags() throws IOException, - AndrolibException { - int size = mIn.readInt(); - if (size < 28) { - throw new AndrolibException("Config size < 28"); - } + private ResConfigFlags readConfigFlags() throws IOException, + AndrolibException { + int size = mIn.readInt(); + if (size < 28) { + throw new AndrolibException("Config size < 28"); + } - boolean isInvalid = false; + boolean isInvalid = false; - short mcc = mIn.readShort(); - short mnc = mIn.readShort(); + short mcc = mIn.readShort(); + short mnc = mIn.readShort(); - char[] language = new char[] { (char) mIn.readByte(), - (char) mIn.readByte() }; - char[] country = new char[] { (char) mIn.readByte(), - (char) mIn.readByte() }; + char[] language = new char[] { (char) mIn.readByte(), + (char) mIn.readByte() }; + char[] country = new char[] { (char) mIn.readByte(), + (char) mIn.readByte() }; - byte orientation = mIn.readByte(); - byte touchscreen = mIn.readByte(); - short density = mIn.readShort(); + byte orientation = mIn.readByte(); + byte touchscreen = mIn.readByte(); - byte keyboard = mIn.readByte(); - byte navigation = mIn.readByte(); - byte inputFlags = mIn.readByte(); + int density = mIn.readUnsignedShort(); + + byte keyboard = mIn.readByte(); + byte navigation = mIn.readByte(); + byte inputFlags = mIn.readByte(); /* inputPad0 */mIn.skipBytes(1); - short screenWidth = mIn.readShort(); - short screenHeight = mIn.readShort(); + short screenWidth = mIn.readShort(); + short screenHeight = mIn.readShort(); - short sdkVersion = mIn.readShort(); + short sdkVersion = mIn.readShort(); /* minorVersion, now must always be 0 */mIn.skipBytes(2); - byte screenLayout = 0; - byte uiMode = 0; - short smallestScreenWidthDp = 0; - if (size >= 32) { - screenLayout = mIn.readByte(); - uiMode = mIn.readByte(); - smallestScreenWidthDp = mIn.readShort(); - } + byte screenLayout = 0; + byte uiMode = 0; + short smallestScreenWidthDp = 0; + if (size >= 32) { + screenLayout = mIn.readByte(); + uiMode = mIn.readByte(); + smallestScreenWidthDp = mIn.readShort(); + } - short screenWidthDp = 0; - short screenHeightDp = 0; + short screenWidthDp = 0; + short screenHeightDp = 0; + if (size >= 36) { + screenWidthDp = mIn.readShort(); + screenHeightDp = mIn.readShort(); + } - if (size >= 36) { - screenWidthDp = mIn.readShort(); - screenHeightDp = mIn.readShort(); - } + short layoutDirection = 0; + if (size >= 38) { + layoutDirection = mIn.readShort(); + } - short layoutDirection = 0; - if (size >= 38 && sdkVersion >= 17 - && !this.mPkg.getName().equalsIgnoreCase("com.htc")) { - layoutDirection = mIn.readShort(); - } + int exceedingSize = size - KNOWN_CONFIG_BYTES; + if (exceedingSize > 0) { + byte[] buf = new byte[exceedingSize]; + mIn.readFully(buf); + BigInteger exceedingBI = new BigInteger(1, buf); - int exceedingSize = size - KNOWN_CONFIG_BYTES; - if (exceedingSize > 0) { - byte[] buf = new byte[exceedingSize]; - mIn.readFully(buf); - BigInteger exceedingBI = new BigInteger(1, buf); + if (exceedingBI.equals(BigInteger.ZERO)) { + LOGGER.fine(String + .format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.", + KNOWN_CONFIG_BYTES)); + } else { + LOGGER.warning(String.format( + "Config flags size > %d. Exceeding bytes: 0x%X.", + KNOWN_CONFIG_BYTES, exceedingBI)); + isInvalid = true; + } + } - if (exceedingBI.equals(BigInteger.ZERO)) { - LOGGER.fine(String - .format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.", - KNOWN_CONFIG_BYTES)); - } else { - LOGGER.warning(String.format( - "Config flags size > %d. Exceeding bytes: 0x%X.", - KNOWN_CONFIG_BYTES, exceedingBI)); - isInvalid = true; - } - } + return new ResConfigFlags(mcc, mnc, language, country, layoutDirection, + orientation, touchscreen, density, keyboard, navigation, + inputFlags, screenWidth, screenHeight, sdkVersion, + screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp, + screenHeightDp, isInvalid); + } - return new ResConfigFlags(mcc, mnc, language, country, layoutDirection, - orientation, touchscreen, density, keyboard, navigation, - inputFlags, screenWidth, screenHeight, sdkVersion, - screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp, - screenHeightDp, isInvalid); - } + private void addMissingResSpecs() throws AndrolibException { + int resId = mResId & 0xffff0000; - private void addMissingResSpecs() throws AndrolibException { - int resId = mResId & 0xffff0000; + for (int i = 0; i < mMissingResSpecs.length; i++) { + if (!mMissingResSpecs[i]) { + continue; + } - for (int i = 0; i < mMissingResSpecs.length; i++) { - if (!mMissingResSpecs[i]) { - continue; - } + ResResSpec spec = new ResResSpec(new ResID(resId | i), + String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType); + mPkg.addResSpec(spec); + mType.addResSpec(spec); - ResResSpec spec = new ResResSpec(new ResID(resId | i), - String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType); - mPkg.addResSpec(spec); - mType.addResSpec(spec); + if (mConfig == null) { + mConfig = mPkg.getOrCreateConfig(new ResConfigFlags()); + } - if (mConfig == null) { - mConfig = mPkg.getOrCreateConfig(new ResConfigFlags()); - } + ResValue value = new ResBoolValue(false, null); + ResResource res = new ResResource(mConfig, spec, value); - ResValue value = new ResBoolValue(false, null); - ResResource res = new ResResource(mConfig, spec, value); + mPkg.addResource(res); + mConfig.addResource(res); + spec.addResource(res); + } + } - mPkg.addResource(res); - mConfig.addResource(res); - spec.addResource(res); - } - } + private Header nextChunk() throws IOException { + return mHeader = Header.read(mIn); + } - private Header nextChunk() throws IOException { - return mHeader = Header.read(mIn); - } + private void checkChunkType(int expectedType) throws AndrolibException { + if (mHeader.type != expectedType) { + throw new AndrolibException(String.format( + "Invalid chunk type: expected=0x%08x, got=0x%08x", + expectedType, mHeader.type)); + } + } - private void checkChunkType(int expectedType) throws AndrolibException { - if (mHeader.type != expectedType) { - throw new AndrolibException(String.format( - "Invalid chunk type: expected=0x%08x, got=0x%08x", - expectedType, mHeader.type)); - } - } + private void nextChunkCheckType(int expectedType) throws IOException, + AndrolibException { + nextChunk(); + checkChunkType(expectedType); + } - private void nextChunkCheckType(int expectedType) throws IOException, - AndrolibException { - nextChunk(); - checkChunkType(expectedType); - } + private final ExtDataInput mIn; + private final ResTable mResTable; + private final CountingInputStream mCountIn; + private final List mFlagsOffsets; + private final boolean mKeepBroken; - private final ExtDataInput mIn; - private final ResTable mResTable; - private final CountingInputStream mCountIn; - private final List mFlagsOffsets; - private final boolean mKeepBroken; + private Header mHeader; + private StringBlock mTableStrings; + private StringBlock mTypeNames; + private StringBlock mSpecNames; + private ResPackage mPkg; + private ResType mType; + private ResConfig mConfig; + private int mResId; + private boolean[] mMissingResSpecs; - private Header mHeader; - private StringBlock mTableStrings; - private StringBlock mTypeNames; - private StringBlock mSpecNames; - private ResPackage mPkg; - private ResType mType; - private ResConfig mConfig; - private int mResId; - private boolean[] mMissingResSpecs; + private final static short ENTRY_FLAG_COMPLEX = 0x0001; - private final static short ENTRY_FLAG_COMPLEX = 0x0001; + public static class Header { + public final short type; + public final int chunkSize; - public static class Header { - public final short type; - public final int chunkSize; + public Header(short type, int size) { + this.type = type; + this.chunkSize = size; + } - public Header(short type, int size) { - this.type = type; - this.chunkSize = size; - } + public static Header read(ExtDataInput in) throws IOException { + short type; + try { + type = in.readShort(); + } catch (EOFException ex) { + return new Header(TYPE_NONE, 0); + } + in.skipBytes(2); + return new Header(type, in.readInt()); + } - public static Header read(ExtDataInput in) throws IOException { - short type; - try { - type = in.readShort(); - } catch (EOFException ex) { - return new Header(TYPE_NONE, 0); - } - in.skipBytes(2); - return new Header(type, in.readInt()); - } + public final static short TYPE_NONE = -1, TYPE_TABLE = 0x0002, + TYPE_PACKAGE = 0x0200, TYPE_TYPE = 0x0202, + TYPE_CONFIG = 0x0201; + } - public final static short TYPE_NONE = -1, TYPE_TABLE = 0x0002, - TYPE_PACKAGE = 0x0200, TYPE_TYPE = 0x0202, - TYPE_CONFIG = 0x0201; - } + public static class FlagsOffset { + public final int offset; + public final int count; - public static class FlagsOffset { - public final int offset; - public final int count; + public FlagsOffset(int offset, int count) { + this.offset = offset; + this.count = count; + } + } - public FlagsOffset(int offset, int count) { - this.offset = offset; - this.count = count; - } - } + private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class + .getName()); + private static final int KNOWN_CONFIG_BYTES = 38; - private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class - .getName()); - private static final int KNOWN_CONFIG_BYTES = 38; + public static class ARSCData { - public static class ARSCData { + public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets, + ResTable resTable) { + mPackages = packages; + mFlagsOffsets = flagsOffsets; + mResTable = resTable; + } - public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets, - ResTable resTable) { - mPackages = packages; - mFlagsOffsets = flagsOffsets; - mResTable = resTable; - } + public FlagsOffset[] getFlagsOffsets() { + return mFlagsOffsets; + } - public FlagsOffset[] getFlagsOffsets() { - return mFlagsOffsets; - } + public ResPackage[] getPackages() { + return mPackages; + } - public ResPackage[] getPackages() { - return mPackages; - } + public ResPackage getOnePackage() throws AndrolibException { + if (mPackages.length <= 0) { + throw new AndrolibException( + "Arsc file contains zero packages"); + } else if (mPackages.length != 1) { + int id = findPackageWithMostResSpecs(); + LOGGER.warning("Arsc file contains multiple packages. Using package " + mPackages[id].getName() + " as default."); + return mPackages[id]; + } + return mPackages[0]; + } - public ResPackage getOnePackage() throws AndrolibException { - if (mPackages.length != 1) { - throw new AndrolibException( - "Arsc file contains zero or multiple packages"); - } - return mPackages[0]; - } + public int findPackageWithMostResSpecs() { + int count = -1; + int id = 0; - public ResTable getResTable() { - return mResTable; - } + // set starting point to package id 0. + count = mPackages[0].getResSpecCount(); - private final ResPackage[] mPackages; - private final FlagsOffset[] mFlagsOffsets; - private final ResTable mResTable; - } -} \ No newline at end of file + // loop through packages looking for largest + for (int i = 0; i < mPackages.length; i++) { + if (mPackages[i].getResSpecCount() >= count) { + count = mPackages[i].getResSpecCount(); + id = i; + } + } + + return id; + } + + public ResTable getResTable() { + return mResTable; + } + + private final ResPackage[] mPackages; + private final FlagsOffset[] mFlagsOffsets; + private final ResTable mResTable; + } +} diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java index aee643a5..17a21644 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java @@ -31,1060 +31,999 @@ import org.xmlpull.v1.XmlPullParserException; /** * @author Ryszard Wiśniewski * @author Dmitry Skiba - * + * * Binary xml files parser. - * + * * Parser has only two states: (1) Operational state, which parser * obtains after first successful call to next() and retains until * open(), close(), or failed call to next(). (2) Closed state, which * parser obtains after open(), close(), or failed call to next(). In * this state methods return invalid values or throw exceptions. - * + * * TODO: * check all methods in closed state - * + * */ public class AXmlResourceParser implements XmlResourceParser { - public AXmlResourceParser() { - resetEventInfo(); - } - - public AXmlResourceParser(InputStream stream) { - this(); - open(stream); - } - - public AndrolibException getFirstError() { - return mFirstError; - } - - public ResAttrDecoder getAttrDecoder() { - return mAttrDecoder; - } - - public void setAttrDecoder(ResAttrDecoder attrDecoder) { - mAttrDecoder = attrDecoder; - } - - public void open(InputStream stream) { - close(); - if (stream != null) { - m_reader = new ExtDataInput(new LEDataInputStream(stream)); - } - } - - @Override - public void close() { - if (!m_operational) { - return; - } - m_operational = false; - // m_reader.close(); - m_reader = null; - m_strings = null; - m_resourceIDs = null; - m_namespaces.reset(); - resetEventInfo(); - } - - // ///////////////////////////////// iteration - @Override - public int next() throws XmlPullParserException, IOException { - if (m_reader == null) { - throw new XmlPullParserException("Parser is not opened.", this, - null); - } - try { - doNext(); - return m_event; - } catch (IOException e) { - close(); - throw e; - } - } - - @Override - public int nextToken() throws XmlPullParserException, IOException { - return next(); - } - - @Override - public int nextTag() throws XmlPullParserException, IOException { - int eventType = next(); - if (eventType == TEXT && isWhitespace()) { - eventType = next(); - } - if (eventType != START_TAG && eventType != END_TAG) { - throw new XmlPullParserException("Expected start or end tag.", - this, null); - } - return eventType; - } - - @Override - public String nextText() throws XmlPullParserException, IOException { - if (getEventType() != START_TAG) { - throw new XmlPullParserException( - "Parser must be on START_TAG to read next text.", this, - null); - } - int eventType = next(); - if (eventType == TEXT) { - String result = getText(); - eventType = next(); - if (eventType != END_TAG) { - throw new XmlPullParserException( - "Event TEXT must be immediately followed by END_TAG.", - this, null); - } - return result; - } else if (eventType == END_TAG) { - return ""; - } else { - throw new XmlPullParserException( - "Parser must be on START_TAG or TEXT to read text.", this, - null); - } - } - - @Override - public void require(int type, String namespace, String name) - throws XmlPullParserException, IOException { - if (type != getEventType() - || (namespace != null && !namespace.equals(getNamespace())) - || (name != null && !name.equals(getName()))) { - throw new XmlPullParserException(TYPES[type] + " is expected.", - this, null); - } - } - - @Override - public int getDepth() { - return m_namespaces.getDepth() - 1; - } - - @Override - public int getEventType() throws XmlPullParserException { - return m_event; - } - - @Override - public int getLineNumber() { - return m_lineNumber; - } - - @Override - public String getName() { - if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) { - return null; - } - return m_strings.getString(m_name); - } - - @Override - public String getText() { - if (m_name == -1 || m_event != TEXT) { - return null; - } - return m_strings.getString(m_name); - } - - @Override - public char[] getTextCharacters(int[] holderForStartAndLength) { - String text = getText(); - if (text == null) { - return null; - } - holderForStartAndLength[0] = 0; - holderForStartAndLength[1] = text.length(); - char[] chars = new char[text.length()]; - text.getChars(0, text.length(), chars, 0); - return chars; - } - - @Override - public String getNamespace() { - return m_strings.getString(m_namespaceUri); - } - - @Override - public String getPrefix() { - int prefix = m_namespaces.findPrefix(m_namespaceUri); - return m_strings.getString(prefix); - } - - @Override - public String getPositionDescription() { - return "XML line #" + getLineNumber(); - } - - @Override - public int getNamespaceCount(int depth) throws XmlPullParserException { - return m_namespaces.getAccumulatedCount(depth); - } - - @Override - public String getNamespacePrefix(int pos) throws XmlPullParserException { - int prefix = m_namespaces.getPrefix(pos); - return m_strings.getString(prefix); - } - - @Override - public String getNamespaceUri(int pos) throws XmlPullParserException { - int uri = m_namespaces.getUri(pos); - return m_strings.getString(uri); - } - - // ///////////////////////////////// attributes - @Override - public String getClassAttribute() { - if (m_classAttribute == -1) { - return null; - } - int offset = getAttributeOffset(m_classAttribute); - int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(value); - } - - @Override - public String getIdAttribute() { - if (m_idAttribute == -1) { - return null; - } - int offset = getAttributeOffset(m_idAttribute); - int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - return m_strings.getString(value); - } - - @Override - public int getIdAttributeResourceValue(int defaultValue) { - if (m_idAttribute == -1) { - return defaultValue; - } - int offset = getAttributeOffset(m_idAttribute); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType != TypedValue.TYPE_REFERENCE) { - return defaultValue; - } - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - @Override - public int getStyleAttribute() { - if (m_styleAttribute == -1) { - return 0; - } - int offset = getAttributeOffset(m_styleAttribute); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - @Override - public int getAttributeCount() { - if (m_event != START_TAG) { - return -1; - } - return m_attributes.length / ATTRIBUTE_LENGHT; - } - - @Override - public String getAttributeNamespace(int index) { - int offset = getAttributeOffset(index); - int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; - if (namespace == -1) { - return ""; - } - return m_strings.getString(namespace); - } - - @Override - public String getAttributePrefix(int index) { - int offset = getAttributeOffset(index); - int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; - int prefix = m_namespaces.findPrefix(uri); - if (prefix == -1) { - return ""; - } - return m_strings.getString(prefix); - } - - @Override - public String getAttributeName(int index) { - int offset = getAttributeOffset(index); - int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; - if (name == -1) { - return ""; - } - return m_strings.getString(name); - } - - @Override - public int getAttributeNameResource(int index) { - int offset = getAttributeOffset(index); - int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; - if (m_resourceIDs == null || name < 0 || name >= m_resourceIDs.length) { - return 0; - } - return m_resourceIDs[name]; - } - - @Override - public int getAttributeValueType(int index) { - int offset = getAttributeOffset(index); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - } - - @Override - public int getAttributeValueData(int index) { - int offset = getAttributeOffset(index); - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - - @Override - public String getAttributeValue(int index) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - int valueRaw = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; - - if (mAttrDecoder != null) { - try { - return mAttrDecoder.decode( - valueType, - valueData, - valueRaw == -1 ? null : ResXmlEncoders - .escapeXmlChars(m_strings.getString(valueRaw)), - getAttributeNameResource(index)); - } catch (AndrolibException ex) { - setFirstError(ex); - LOGGER.log(Level.WARNING, String.format( - "Could not decode attr value, using undecoded value " - + "instead: ns=%s, name=%s, value=0x%08x", - getAttributePrefix(index), getAttributeName(index), - valueData), ex); - } - } else { - if (valueType == TypedValue.TYPE_STRING) { - return ResXmlEncoders.escapeXmlChars(m_strings - .getString(valueRaw)); - } - } - - return TypedValue.coerceToString(valueType, valueData); - } - - @Override - public boolean getAttributeBooleanValue(int index, boolean defaultValue) { - return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0; - } - - @Override - public float getAttributeFloatValue(int index, float defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_FLOAT) { - int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - return Float.intBitsToFloat(valueData); - } - return defaultValue; - } - - @Override - public int getAttributeIntValue(int index, int defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType >= TypedValue.TYPE_FIRST_INT - && valueType <= TypedValue.TYPE_LAST_INT) { - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - return defaultValue; - } - - @Override - public int getAttributeUnsignedIntValue(int index, int defaultValue) { - return getAttributeIntValue(index, defaultValue); - } - - @Override - public int getAttributeResourceValue(int index, int defaultValue) { - int offset = getAttributeOffset(index); - int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; - if (valueType == TypedValue.TYPE_REFERENCE) { - return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; - } - return defaultValue; - } - - @Override - public String getAttributeValue(String namespace, String attribute) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return ""; - } - return getAttributeValue(index); - } - - @Override - public boolean getAttributeBooleanValue(String namespace, String attribute, - boolean defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeBooleanValue(index, defaultValue); - } - - @Override - public float getAttributeFloatValue(String namespace, String attribute, - float defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeFloatValue(index, defaultValue); - } - - @Override - public int getAttributeIntValue(String namespace, String attribute, - int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeIntValue(index, defaultValue); - } - - @Override - public int getAttributeUnsignedIntValue(String namespace, String attribute, - int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeUnsignedIntValue(index, defaultValue); - } - - @Override - public int getAttributeResourceValue(String namespace, String attribute, - int defaultValue) { - int index = findAttribute(namespace, attribute); - if (index == -1) { - return defaultValue; - } - return getAttributeResourceValue(index, defaultValue); - } - - @Override - public int getAttributeListValue(int index, String[] options, - int defaultValue) { - // TODO implement - return 0; - } - - @Override - public int getAttributeListValue(String namespace, String attribute, - String[] options, int defaultValue) { - // TODO implement - return 0; - } - - @Override - public String getAttributeType(int index) { - return "CDATA"; - } - - @Override - public boolean isAttributeDefault(int index) { - return false; - } - - // ///////////////////////////////// dummies - @Override - public void setInput(InputStream stream, String inputEncoding) - throws XmlPullParserException { - open(stream); - } - - @Override - public void setInput(Reader reader) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - @Override - public String getInputEncoding() { - return null; - } - - @Override - public int getColumnNumber() { - return -1; - } - - @Override - public boolean isEmptyElementTag() throws XmlPullParserException { - return false; - } - - @Override - public boolean isWhitespace() throws XmlPullParserException { - return false; - } - - @Override - public void defineEntityReplacementText(String entityName, - String replacementText) throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - @Override - public String getNamespace(String prefix) { - throw new RuntimeException(E_NOT_SUPPORTED); - } - - @Override - public Object getProperty(String name) { - return null; - } - - @Override - public void setProperty(String name, Object value) - throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - @Override - public boolean getFeature(String feature) { - return false; - } - - @Override - public void setFeature(String name, boolean value) - throws XmlPullParserException { - throw new XmlPullParserException(E_NOT_SUPPORTED); - } - - // /////////////////////////////////////////// implementation - /** - * Namespace stack, holds prefix+uri pairs, as well as depth information. - * All information is stored in one int[] array. Array consists of depth - * frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count; - * Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable - * bottom-up traversal. increaseDepth adds depth frame, decreaseDepth - * removes it. push/pop operations operate only in current depth frame. - * decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX - * methods search all depth frames starting from the last namespace pair of - * current depth frame. All functions that operate with int, use -1 as - * 'invalid value'. - * - * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! - * - */ - private static final class NamespaceStack { - - public NamespaceStack() { - m_data = new int[32]; - } - - public final void reset() { - m_dataLength = 0; - m_count = 0; - m_depth = 0; - } - - public final int getTotalCount() { - return m_count; - } - - public final int getCurrentCount() { - if (m_dataLength == 0) { - return 0; - } - int offset = m_dataLength - 1; - return m_data[offset]; - } - - public final int getAccumulatedCount(int depth) { - if (m_dataLength == 0 || depth < 0) { - return 0; - } - if (depth > m_depth) { - depth = m_depth; - } - int accumulatedCount = 0; - int offset = 0; - for (; depth != 0; --depth) { - int count = m_data[offset]; - accumulatedCount += count; - offset += (2 + count * 2); - } - return accumulatedCount; - } - - public final void push(int prefix, int uri) { - if (m_depth == 0) { - increaseDepth(); - } - ensureDataCapacity(2); - int offset = m_dataLength - 1; - int count = m_data[offset]; - m_data[offset - 1 - count * 2] = count + 1; - m_data[offset] = prefix; - m_data[offset + 1] = uri; - m_data[offset + 2] = count + 1; - m_dataLength += 2; - m_count += 1; - } - - public final boolean pop(int prefix, int uri) { - if (m_dataLength == 0) { - return false; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) { - if (m_data[o] != prefix || m_data[o + 1] != uri) { - continue; - } - count -= 1; - if (i == 0) { - m_data[o] = count; - o -= (1 + count * 2); - m_data[o] = count; - } else { - m_data[offset] = count; - offset -= (1 + 2 + count * 2); - m_data[offset] = count; - System.arraycopy(m_data, o + 2, m_data, o, m_dataLength - o); - } - m_dataLength -= 2; - m_count -= 1; - return true; - } - return false; - } - - public final boolean pop() { - if (m_dataLength == 0) { - return false; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - if (count == 0) { - return false; - } - count -= 1; - offset -= 2; - m_data[offset] = count; - offset -= (1 + count * 2); - m_data[offset] = count; - m_dataLength -= 2; - m_count -= 1; - return true; - } - - public final int getPrefix(int index) { - return get(index, true); - } - - public final int getUri(int index) { - return get(index, false); - } - - public final int findPrefix(int uri) { - return find(uri, false); - } - - public final int findUri(int prefix) { - return find(prefix, true); - } - - public final int getDepth() { - return m_depth; - } - - public final void increaseDepth() { - ensureDataCapacity(2); - int offset = m_dataLength; - m_data[offset] = 0; - m_data[offset + 1] = 0; - m_dataLength += 2; - m_depth += 1; - } - - public final void decreaseDepth() { - if (m_dataLength == 0) { - return; - } - int offset = m_dataLength - 1; - int count = m_data[offset]; - if ((offset - 1 - count * 2) == 0) { - return; - } - m_dataLength -= 2 + count * 2; - m_count -= count; - m_depth -= 1; - } - - private void ensureDataCapacity(int capacity) { - int available = (m_data.length - m_dataLength); - if (available > capacity) { - return; - } - int newLength = (m_data.length + available) * 2; - int[] newData = new int[newLength]; - System.arraycopy(m_data, 0, newData, 0, m_dataLength); - m_data = newData; - } - - private final int find(int prefixOrUri, boolean prefix) { - if (m_dataLength == 0) { - return -1; - } - int offset = m_dataLength - 1; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; - offset -= 2; - for (; count != 0; --count) { - if (prefix) { - if (m_data[offset] == prefixOrUri) { - return m_data[offset + 1]; - } - } else { - if (m_data[offset + 1] == prefixOrUri) { - return m_data[offset]; - } - } - offset -= 2; - } - } - return -1; - } - - private final int get(int index, boolean prefix) { - if (m_dataLength == 0 || index < 0) { - return -1; - } - int offset = 0; - for (int i = m_depth; i != 0; --i) { - int count = m_data[offset]; - if (index >= count) { - index -= count; - offset += (2 + count * 2); - continue; - } - offset += (1 + index * 2); - if (!prefix) { - offset += 1; - } - return m_data[offset]; - } - return -1; - } - - private int[] m_data; - private int m_dataLength; - private int m_count; - private int m_depth; - } - - // ///////////////////////////////// package-visible - // final void fetchAttributes(int[] styleableIDs,TypedArray result) { - // result.resetIndices(); - // if (m_attributes==null || m_resourceIDs==null) { - // return; - // } - // boolean needStrings=false; - // for (int i=0,e=styleableIDs.length;i!=e;++i) { - // int id=styleableIDs[i]; - // for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) { - // int name=m_attributes[o+ATTRIBUTE_IX_NAME]; - // if (name>=m_resourceIDs.length || - // m_resourceIDs[name]!=id) - // { - // continue; - // } - // int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE]; - // int valueData; - // int assetCookie; - // if (valueType==TypedValue.TYPE_STRING) { - // valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING]; - // assetCookie=-1; - // needStrings=true; - // } else { - // valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA]; - // assetCookie=0; - // } - // result.addValue(i,valueType,valueData,assetCookie,id,0); - // } - // } - // if (needStrings) { - // result.setStrings(m_strings); - // } - // } - final StringBlock getStrings() { - return m_strings; - } - - // ///////////////////////////////// - private final int getAttributeOffset(int index) { - if (m_event != START_TAG) { - throw new IndexOutOfBoundsException( - "Current event is not START_TAG."); - } - int offset = index * ATTRIBUTE_LENGHT; - if (offset >= m_attributes.length) { - throw new IndexOutOfBoundsException("Invalid attribute index (" - + index + ")."); - } - return offset; - } - - private final int findAttribute(String namespace, String attribute) { - if (m_strings == null || attribute == null) { - return -1; - } - int name = m_strings.find(attribute); - if (name == -1) { - return -1; - } - int uri = (namespace != null) ? m_strings.find(namespace) : -1; - for (int o = 0; o != m_attributes.length; o += ATTRIBUTE_LENGHT) { - if (name == m_attributes[o + ATTRIBUTE_IX_NAME] - && (uri == -1 || uri == m_attributes[o - + ATTRIBUTE_IX_NAMESPACE_URI])) { - return o / ATTRIBUTE_LENGHT; - } - } - return -1; - } - - private final void resetEventInfo() { - m_event = -1; - m_lineNumber = -1; - m_name = -1; - m_namespaceUri = -1; - m_attributes = null; - m_idAttribute = -1; - m_classAttribute = -1; - m_styleAttribute = -1; - } - - private final void doNext() throws IOException { - // Delayed initialization. - if (m_strings == null) { - m_reader.skipCheckInt(CHUNK_AXML_FILE); + public AXmlResourceParser() { + resetEventInfo(); + } + + public AXmlResourceParser(InputStream stream) { + this(); + open(stream); + } + + public AndrolibException getFirstError() { + return mFirstError; + } + + public ResAttrDecoder getAttrDecoder() { + return mAttrDecoder; + } + + public void setAttrDecoder(ResAttrDecoder attrDecoder) { + mAttrDecoder = attrDecoder; + } + + public void open(InputStream stream) { + close(); + if (stream != null) { + m_reader = new ExtDataInput(new LEDataInputStream(stream)); + } + } + + @Override + public void close() { + if (!m_operational) { + return; + } + m_operational = false; + // m_reader.close(); + m_reader = null; + m_strings = null; + m_resourceIDs = null; + m_namespaces.reset(); + resetEventInfo(); + } + + // ///////////////////////////////// iteration + @Override + public int next() throws XmlPullParserException, IOException { + if (m_reader == null) { + throw new XmlPullParserException("Parser is not opened.", this, + null); + } + try { + doNext(); + return m_event; + } catch (IOException e) { + close(); + throw e; + } + } + + @Override + public int nextToken() throws XmlPullParserException, IOException { + return next(); + } + + @Override + public int nextTag() throws XmlPullParserException, IOException { + int eventType = next(); + if (eventType == TEXT && isWhitespace()) { + eventType = next(); + } + if (eventType != START_TAG && eventType != END_TAG) { + throw new XmlPullParserException("Expected start or end tag.", + this, null); + } + return eventType; + } + + @Override + public String nextText() throws XmlPullParserException, IOException { + if (getEventType() != START_TAG) { + throw new XmlPullParserException( + "Parser must be on START_TAG to read next text.", this, + null); + } + int eventType = next(); + if (eventType == TEXT) { + String result = getText(); + eventType = next(); + if (eventType != END_TAG) { + throw new XmlPullParserException( + "Event TEXT must be immediately followed by END_TAG.", + this, null); + } + return result; + } else if (eventType == END_TAG) { + return ""; + } else { + throw new XmlPullParserException( + "Parser must be on START_TAG or TEXT to read text.", this, + null); + } + } + + @Override + public void require(int type, String namespace, String name) + throws XmlPullParserException, IOException { + if (type != getEventType() + || (namespace != null && !namespace.equals(getNamespace())) + || (name != null && !name.equals(getName()))) { + throw new XmlPullParserException(TYPES[type] + " is expected.", + this, null); + } + } + + @Override + public int getDepth() { + return m_namespaces.getDepth() - 1; + } + + @Override + public int getEventType() throws XmlPullParserException { + return m_event; + } + + @Override + public int getLineNumber() { + return m_lineNumber; + } + + @Override + public String getName() { + if (m_name == -1 || (m_event != START_TAG && m_event != END_TAG)) { + return null; + } + return m_strings.getString(m_name); + } + + @Override + public String getText() { + if (m_name == -1 || m_event != TEXT) { + return null; + } + return m_strings.getString(m_name); + } + + @Override + public char[] getTextCharacters(int[] holderForStartAndLength) { + String text = getText(); + if (text == null) { + return null; + } + holderForStartAndLength[0] = 0; + holderForStartAndLength[1] = text.length(); + char[] chars = new char[text.length()]; + text.getChars(0, text.length(), chars, 0); + return chars; + } + + @Override + public String getNamespace() { + return m_strings.getString(m_namespaceUri); + } + + @Override + public String getPrefix() { + int prefix = m_namespaces.findPrefix(m_namespaceUri); + return m_strings.getString(prefix); + } + + @Override + public String getPositionDescription() { + return "XML line #" + getLineNumber(); + } + + @Override + public int getNamespaceCount(int depth) throws XmlPullParserException { + return m_namespaces.getAccumulatedCount(depth); + } + + @Override + public String getNamespacePrefix(int pos) throws XmlPullParserException { + int prefix = m_namespaces.getPrefix(pos); + return m_strings.getString(prefix); + } + + @Override + public String getNamespaceUri(int pos) throws XmlPullParserException { + int uri = m_namespaces.getUri(pos); + return m_strings.getString(uri); + } + + // ///////////////////////////////// attributes + @Override + public String getClassAttribute() { + if (m_classAttribute == -1) { + return null; + } + int offset = getAttributeOffset(m_classAttribute); + int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + return m_strings.getString(value); + } + + @Override + public String getIdAttribute() { + if (m_idAttribute == -1) { + return null; + } + int offset = getAttributeOffset(m_idAttribute); + int value = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + return m_strings.getString(value); + } + + @Override + public int getIdAttributeResourceValue(int defaultValue) { + if (m_idAttribute == -1) { + return defaultValue; + } + int offset = getAttributeOffset(m_idAttribute); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType != TypedValue.TYPE_REFERENCE) { + return defaultValue; + } + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + @Override + public int getStyleAttribute() { + if (m_styleAttribute == -1) { + return 0; + } + int offset = getAttributeOffset(m_styleAttribute); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + @Override + public int getAttributeCount() { + if (m_event != START_TAG) { + return -1; + } + return m_attributes.length / ATTRIBUTE_LENGTH; + } + + @Override + public String getAttributeNamespace(int index) { + int offset = getAttributeOffset(index); + int namespace = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; + if (namespace == -1) { + return ""; + } + return m_strings.getString(namespace); + } + + @Override + public String getAttributePrefix(int index) { + int offset = getAttributeOffset(index); + int uri = m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]; + int prefix = m_namespaces.findPrefix(uri); + if (prefix == -1) { + return ""; + } + return m_strings.getString(prefix); + } + + @Override + public String getAttributeName(int index) { + int offset = getAttributeOffset(index); + int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; + if (name == -1) { + return ""; + } + return m_strings.getString(name); + } + + @Override + public int getAttributeNameResource(int index) { + int offset = getAttributeOffset(index); + int name = m_attributes[offset + ATTRIBUTE_IX_NAME]; + if (m_resourceIDs == null || name < 0 || name >= m_resourceIDs.length) { + return 0; + } + return m_resourceIDs[name]; + } + + @Override + public int getAttributeValueType(int index) { + int offset = getAttributeOffset(index); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + } + + @Override + public int getAttributeValueData(int index) { + int offset = getAttributeOffset(index); + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + + @Override + public String getAttributeValue(int index) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + int valueRaw = m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]; + + if (mAttrDecoder != null) { + try { + return mAttrDecoder.decode( + valueType, + valueData, + valueRaw == -1 ? null : ResXmlEncoders + .escapeXmlChars(m_strings.getString(valueRaw)), + getAttributeNameResource(index)); + } catch (AndrolibException ex) { + setFirstError(ex); + LOGGER.log(Level.WARNING, String.format( + "Could not decode attr value, using undecoded value " + + "instead: ns=%s, name=%s, value=0x%08x", + getAttributePrefix(index), getAttributeName(index), + valueData), ex); + } + } + return TypedValue.coerceToString(valueType, valueData); + } + + @Override + public boolean getAttributeBooleanValue(int index, boolean defaultValue) { + return getAttributeIntValue(index, defaultValue ? 1 : 0) != 0; + } + + @Override + public float getAttributeFloatValue(int index, float defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType == TypedValue.TYPE_FLOAT) { + int valueData = m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + return Float.intBitsToFloat(valueData); + } + return defaultValue; + } + + @Override + public int getAttributeIntValue(int index, int defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType >= TypedValue.TYPE_FIRST_INT + && valueType <= TypedValue.TYPE_LAST_INT) { + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + return defaultValue; + } + + @Override + public int getAttributeUnsignedIntValue(int index, int defaultValue) { + return getAttributeIntValue(index, defaultValue); + } + + @Override + public int getAttributeResourceValue(int index, int defaultValue) { + int offset = getAttributeOffset(index); + int valueType = m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]; + if (valueType == TypedValue.TYPE_REFERENCE) { + return m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]; + } + return defaultValue; + } + + @Override + public String getAttributeValue(String namespace, String attribute) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return ""; + } + return getAttributeValue(index); + } + + @Override + public boolean getAttributeBooleanValue(String namespace, String attribute, + boolean defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeBooleanValue(index, defaultValue); + } + + @Override + public float getAttributeFloatValue(String namespace, String attribute, + float defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeFloatValue(index, defaultValue); + } + + @Override + public int getAttributeIntValue(String namespace, String attribute, + int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeIntValue(index, defaultValue); + } + + @Override + public int getAttributeUnsignedIntValue(String namespace, String attribute, + int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeUnsignedIntValue(index, defaultValue); + } + + @Override + public int getAttributeResourceValue(String namespace, String attribute, + int defaultValue) { + int index = findAttribute(namespace, attribute); + if (index == -1) { + return defaultValue; + } + return getAttributeResourceValue(index, defaultValue); + } + + @Override + public int getAttributeListValue(int index, String[] options, + int defaultValue) { + // TODO implement + return 0; + } + + @Override + public int getAttributeListValue(String namespace, String attribute, + String[] options, int defaultValue) { + // TODO implement + return 0; + } + + @Override + public String getAttributeType(int index) { + return "CDATA"; + } + + @Override + public boolean isAttributeDefault(int index) { + return false; + } + + // ///////////////////////////////// dummies + @Override + public void setInput(InputStream stream, String inputEncoding) + throws XmlPullParserException { + open(stream); + } + + @Override + public void setInput(Reader reader) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + @Override + public String getInputEncoding() { + return null; + } + + @Override + public int getColumnNumber() { + return -1; + } + + @Override + public boolean isEmptyElementTag() throws XmlPullParserException { + return false; + } + + @Override + public boolean isWhitespace() throws XmlPullParserException { + return false; + } + + @Override + public void defineEntityReplacementText(String entityName, + String replacementText) throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + @Override + public String getNamespace(String prefix) { + throw new RuntimeException(E_NOT_SUPPORTED); + } + + @Override + public Object getProperty(String name) { + return null; + } + + @Override + public void setProperty(String name, Object value) + throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + @Override + public boolean getFeature(String feature) { + return false; + } + + @Override + public void setFeature(String name, boolean value) + throws XmlPullParserException { + throw new XmlPullParserException(E_NOT_SUPPORTED); + } + + // /////////////////////////////////////////// implementation + /** + * Namespace stack, holds prefix+uri pairs, as well as depth information. + * All information is stored in one int[] array. Array consists of depth + * frames: Data=DepthFrame*; DepthFrame=Count+[Prefix+Uri]*+Count; + * Count='count of Prefix+Uri pairs'; Yes, count is stored twice, to enable + * bottom-up traversal. increaseDepth adds depth frame, decreaseDepth + * removes it. push/pop operations operate only in current depth frame. + * decreaseDepth removes any remaining (not pop'ed) namespace pairs. findXXX + * methods search all depth frames starting from the last namespace pair of + * current depth frame. All functions that operate with int, use -1 as + * 'invalid value'. + * + * !! functions expect 'prefix'+'uri' pairs, not 'uri'+'prefix' !! + * + */ + private static final class NamespaceStack { + + public NamespaceStack() { + m_data = new int[32]; + } + + public final void reset() { + m_dataLength = 0; + m_count = 0; + m_depth = 0; + } + + public final int getTotalCount() { + return m_count; + } + + public final int getCurrentCount() { + if (m_dataLength == 0) { + return 0; + } + int offset = m_dataLength - 1; + return m_data[offset]; + } + + public final int getAccumulatedCount(int depth) { + if (m_dataLength == 0 || depth < 0) { + return 0; + } + if (depth > m_depth) { + depth = m_depth; + } + int accumulatedCount = 0; + int offset = 0; + for (; depth != 0; --depth) { + int count = m_data[offset]; + accumulatedCount += count; + offset += (2 + count * 2); + } + return accumulatedCount; + } + + public final void push(int prefix, int uri) { + if (m_depth == 0) { + increaseDepth(); + } + ensureDataCapacity(2); + int offset = m_dataLength - 1; + int count = m_data[offset]; + m_data[offset - 1 - count * 2] = count + 1; + m_data[offset] = prefix; + m_data[offset + 1] = uri; + m_data[offset + 2] = count + 1; + m_dataLength += 2; + m_count += 1; + } + + public final boolean pop(int prefix, int uri) { + if (m_dataLength == 0) { + return false; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + for (int i = 0, o = offset - 2; i != count; ++i, o -= 2) { + if (m_data[o] != prefix || m_data[o + 1] != uri) { + continue; + } + count -= 1; + if (i == 0) { + m_data[o] = count; + o -= (1 + count * 2); + m_data[o] = count; + } else { + m_data[offset] = count; + offset -= (1 + 2 + count * 2); + m_data[offset] = count; + System.arraycopy(m_data, o + 2, m_data, o, m_dataLength - o); + } + m_dataLength -= 2; + m_count -= 1; + return true; + } + return false; + } + + public final boolean pop() { + if (m_dataLength == 0) { + return false; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + if (count == 0) { + return false; + } + count -= 1; + offset -= 2; + m_data[offset] = count; + offset -= (1 + count * 2); + m_data[offset] = count; + m_dataLength -= 2; + m_count -= 1; + return true; + } + + public final int getPrefix(int index) { + return get(index, true); + } + + public final int getUri(int index) { + return get(index, false); + } + + public final int findPrefix(int uri) { + return find(uri, false); + } + + public final int findUri(int prefix) { + return find(prefix, true); + } + + public final int getDepth() { + return m_depth; + } + + public final void increaseDepth() { + ensureDataCapacity(2); + int offset = m_dataLength; + m_data[offset] = 0; + m_data[offset + 1] = 0; + m_dataLength += 2; + m_depth += 1; + } + + public final void decreaseDepth() { + if (m_dataLength == 0) { + return; + } + int offset = m_dataLength - 1; + int count = m_data[offset]; + if ((offset - 1 - count * 2) == 0) { + return; + } + m_dataLength -= 2 + count * 2; + m_count -= count; + m_depth -= 1; + } + + private void ensureDataCapacity(int capacity) { + int available = (m_data.length - m_dataLength); + if (available > capacity) { + return; + } + int newLength = (m_data.length + available) * 2; + int[] newData = new int[newLength]; + System.arraycopy(m_data, 0, newData, 0, m_dataLength); + m_data = newData; + } + + private final int find(int prefixOrUri, boolean prefix) { + if (m_dataLength == 0) { + return -1; + } + int offset = m_dataLength - 1; + for (int i = m_depth; i != 0; --i) { + int count = m_data[offset]; + offset -= 2; + for (; count != 0; --count) { + if (prefix) { + if (m_data[offset] == prefixOrUri) { + return m_data[offset + 1]; + } + } else { + if (m_data[offset + 1] == prefixOrUri) { + return m_data[offset]; + } + } + offset -= 2; + } + } + return -1; + } + + private final int get(int index, boolean prefix) { + if (m_dataLength == 0 || index < 0) { + return -1; + } + int offset = 0; + for (int i = m_depth; i != 0; --i) { + int count = m_data[offset]; + if (index >= count) { + index -= count; + offset += (2 + count * 2); + continue; + } + offset += (1 + index * 2); + if (!prefix) { + offset += 1; + } + return m_data[offset]; + } + return -1; + } + + private int[] m_data; + private int m_dataLength; + private int m_count; + private int m_depth; + } + + // ///////////////////////////////// package-visible + // final void fetchAttributes(int[] styleableIDs,TypedArray result) { + // result.resetIndices(); + // if (m_attributes==null || m_resourceIDs==null) { + // return; + // } + // boolean needStrings=false; + // for (int i=0,e=styleableIDs.length;i!=e;++i) { + // int id=styleableIDs[i]; + // for (int o=0;o!=m_attributes.length;o+=ATTRIBUTE_LENGHT) { + // int name=m_attributes[o+ATTRIBUTE_IX_NAME]; + // if (name>=m_resourceIDs.length || + // m_resourceIDs[name]!=id) + // { + // continue; + // } + // int valueType=m_attributes[o+ATTRIBUTE_IX_VALUE_TYPE]; + // int valueData; + // int assetCookie; + // if (valueType==TypedValue.TYPE_STRING) { + // valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_STRING]; + // assetCookie=-1; + // needStrings=true; + // } else { + // valueData=m_attributes[o+ATTRIBUTE_IX_VALUE_DATA]; + // assetCookie=0; + // } + // result.addValue(i,valueType,valueData,assetCookie,id,0); + // } + // } + // if (needStrings) { + // result.setStrings(m_strings); + // } + // } + final StringBlock getStrings() { + return m_strings; + } + + // ///////////////////////////////// + private final int getAttributeOffset(int index) { + if (m_event != START_TAG) { + throw new IndexOutOfBoundsException( + "Current event is not START_TAG."); + } + int offset = index * ATTRIBUTE_LENGTH; + if (offset >= m_attributes.length) { + throw new IndexOutOfBoundsException("Invalid attribute index (" + + index + ")."); + } + return offset; + } + + private final int findAttribute(String namespace, String attribute) { + if (m_strings == null || attribute == null) { + return -1; + } + int name = m_strings.find(attribute); + if (name == -1) { + return -1; + } + int uri = (namespace != null) ? m_strings.find(namespace) : -1; + for (int o = 0; o != m_attributes.length; o += ATTRIBUTE_LENGTH) { + if (name == m_attributes[o + ATTRIBUTE_IX_NAME] + && (uri == -1 || uri == m_attributes[o + + ATTRIBUTE_IX_NAMESPACE_URI])) { + return o / ATTRIBUTE_LENGTH; + } + } + return -1; + } + + private final void resetEventInfo() { + m_event = -1; + m_lineNumber = -1; + m_name = -1; + m_namespaceUri = -1; + m_attributes = null; + m_idAttribute = -1; + m_classAttribute = -1; + m_styleAttribute = -1; + } + + private final void doNext() throws IOException { + // Delayed initialization. + if (m_strings == null) { + m_reader.skipCheckInt(CHUNK_AXML_FILE); /* * chunkSize - */m_reader.skipInt(); - m_strings = StringBlock.read(m_reader); - m_namespaces.increaseDepth(); - m_operational = true; - } + */ + m_reader.skipInt(); + m_strings = StringBlock.read(m_reader); + m_namespaces.increaseDepth(); + m_operational = true; + } - if (m_event == END_DOCUMENT) { - return; - } + if (m_event == END_DOCUMENT) { + return; + } - int event = m_event; - resetEventInfo(); + int event = m_event; + resetEventInfo(); - while (true) { - if (m_decreaseDepth) { - m_decreaseDepth = false; - m_namespaces.decreaseDepth(); - } + while (true) { + if (m_decreaseDepth) { + m_decreaseDepth = false; + m_namespaces.decreaseDepth(); + } - // Fake END_DOCUMENT event. - if (event == END_TAG && m_namespaces.getDepth() == 1 - && m_namespaces.getCurrentCount() == 0) { - m_event = END_DOCUMENT; - break; - } + // Fake END_DOCUMENT event. + if (event == END_TAG && m_namespaces.getDepth() == 1 + && m_namespaces.getCurrentCount() == 0) { + m_event = END_DOCUMENT; + break; + } - int chunkType; - if (event == START_DOCUMENT) { - // Fake event, see CHUNK_XML_START_TAG handler. - chunkType = CHUNK_XML_START_TAG; - } else { - chunkType = m_reader.readInt(); - } + int chunkType; + if (event == START_DOCUMENT) { + // Fake event, see CHUNK_XML_START_TAG handler. + chunkType = CHUNK_XML_START_TAG; + } else { + chunkType = m_reader.readInt(); + } - if (chunkType == CHUNK_RESOURCEIDS) { - int chunkSize = m_reader.readInt(); - if (chunkSize < 8 || (chunkSize % 4) != 0) { - throw new IOException("Invalid resource ids size (" - + chunkSize + ")."); - } - m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2); - continue; - } + if (chunkType == CHUNK_RESOURCEIDS) { + int chunkSize = m_reader.readInt(); + if (chunkSize < 8 || (chunkSize % 4) != 0) { + throw new IOException("Invalid resource ids size (" + + chunkSize + ")."); + } + m_resourceIDs = m_reader.readIntArray(chunkSize / 4 - 2); + continue; + } - if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) { - throw new IOException("Invalid chunk type (" + chunkType + ")."); - } + if (chunkType < CHUNK_XML_FIRST || chunkType > CHUNK_XML_LAST) { + throw new IOException("Invalid chunk type (" + chunkType + ")."); + } - // Fake START_DOCUMENT event. - if (chunkType == CHUNK_XML_START_TAG && event == -1) { - m_event = START_DOCUMENT; - break; - } + // Fake START_DOCUMENT event. + if (chunkType == CHUNK_XML_START_TAG && event == -1) { + m_event = START_DOCUMENT; + break; + } - // Common header. + // Common header. /* chunkSize */m_reader.skipInt(); - int lineNumber = m_reader.readInt(); + int lineNumber = m_reader.readInt(); /* 0xFFFFFFFF */m_reader.skipInt(); - if (chunkType == CHUNK_XML_START_NAMESPACE - || chunkType == CHUNK_XML_END_NAMESPACE) { - if (chunkType == CHUNK_XML_START_NAMESPACE) { - int prefix = m_reader.readInt(); - int uri = m_reader.readInt(); - m_namespaces.push(prefix, uri); - } else { + if (chunkType == CHUNK_XML_START_NAMESPACE + || chunkType == CHUNK_XML_END_NAMESPACE) { + if (chunkType == CHUNK_XML_START_NAMESPACE) { + int prefix = m_reader.readInt(); + int uri = m_reader.readInt(); + m_namespaces.push(prefix, uri); + } else { /* prefix */m_reader.skipInt(); /* uri */m_reader.skipInt(); - m_namespaces.pop(); - } - continue; - } + m_namespaces.pop(); + } + continue; + } - m_lineNumber = lineNumber; + m_lineNumber = lineNumber; - if (chunkType == CHUNK_XML_START_TAG) { - m_namespaceUri = m_reader.readInt(); - m_name = m_reader.readInt(); + if (chunkType == CHUNK_XML_START_TAG) { + m_namespaceUri = m_reader.readInt(); + m_name = m_reader.readInt(); /* flags? */m_reader.skipInt(); - int attributeCount = m_reader.readInt(); - m_idAttribute = (attributeCount >>> 16) - 1; - attributeCount &= 0xFFFF; - m_classAttribute = m_reader.readInt(); - m_styleAttribute = (m_classAttribute >>> 16) - 1; - m_classAttribute = (m_classAttribute & 0xFFFF) - 1; - m_attributes = m_reader.readIntArray(attributeCount - * ATTRIBUTE_LENGHT); - for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) { - m_attributes[i] = (m_attributes[i] >>> 24); - i += ATTRIBUTE_LENGHT; - } - m_namespaces.increaseDepth(); - m_event = START_TAG; - break; - } + int attributeCount = m_reader.readInt(); + m_idAttribute = (attributeCount >>> 16) - 1; + attributeCount &= 0xFFFF; + m_classAttribute = m_reader.readInt(); + m_styleAttribute = (m_classAttribute >>> 16) - 1; + m_classAttribute = (m_classAttribute & 0xFFFF) - 1; + m_attributes = m_reader.readIntArray(attributeCount + * ATTRIBUTE_LENGTH); + for (int i = ATTRIBUTE_IX_VALUE_TYPE; i < m_attributes.length;) { + m_attributes[i] = (m_attributes[i] >>> 24); + i += ATTRIBUTE_LENGTH; + } + m_namespaces.increaseDepth(); + m_event = START_TAG; + break; + } - if (chunkType == CHUNK_XML_END_TAG) { - m_namespaceUri = m_reader.readInt(); - m_name = m_reader.readInt(); - m_event = END_TAG; - m_decreaseDepth = true; - break; - } + if (chunkType == CHUNK_XML_END_TAG) { + m_namespaceUri = m_reader.readInt(); + m_name = m_reader.readInt(); + m_event = END_TAG; + m_decreaseDepth = true; + break; + } - if (chunkType == CHUNK_XML_TEXT) { - m_name = m_reader.readInt(); + if (chunkType == CHUNK_XML_TEXT) { + m_name = m_reader.readInt(); /* ? */m_reader.skipInt(); /* ? */m_reader.skipInt(); - m_event = TEXT; - break; - } - } - } + m_event = TEXT; + break; + } + } + } - private static String formatArray(int[] array, int min, int max) { - if (max > array.length) { - max = array.length; - } - if (min < 0) { - min = 0; - } - StringBuffer sb = new StringBuffer("["); - int i = min; - while (true) { - sb.append(array[i]); - i++; - if (i < max) { - sb.append(", "); - } else { - sb.append("]"); - break; - } - } - return sb.toString(); - } + private void setFirstError(AndrolibException error) { + if (mFirstError == null) { + mFirstError = error; + } + } - private boolean compareAttr(int[] attr1, int[] attr2) { - // TODO: sort Attrs - /* - * ATTRIBUTE_IX_VALUE_TYPE == TYPE_STRING : ATTRIBUTE_IX_VALUE_STRING : - * ATTRIBUTE_IX_NAMESPACE_URI ATTRIBUTE_IX_NAMESPACE_URI : - * ATTRIBUTE_IX_NAME id - */ - if (attr1[ATTRIBUTE_IX_VALUE_TYPE] == TypedValue.TYPE_STRING - && attr1[ATTRIBUTE_IX_VALUE_TYPE] == attr2[ATTRIBUTE_IX_VALUE_TYPE] - && // (m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) - // || - // m_strings.touch(attr2[ATTRIBUTE_IX_VALUE_STRING], - // m_name)) && - // m_strings.touch(attr1[ATTRIBUTE_IX_VALUE_STRING], m_name) - // && - attr1[ATTRIBUTE_IX_VALUE_STRING] != attr2[ATTRIBUTE_IX_VALUE_STRING]) { - return (attr1[ATTRIBUTE_IX_VALUE_STRING] < attr2[ATTRIBUTE_IX_VALUE_STRING]); - } else if ((attr1[ATTRIBUTE_IX_NAMESPACE_URI] == attr2[ATTRIBUTE_IX_NAMESPACE_URI]) - && (attr1[ATTRIBUTE_IX_NAMESPACE_URI] != -1) && // (m_strings.touch(attr1[ATTRIBUTE_IX_NAME], - // m_name) || - // m_strings.touch(attr2[ATTRIBUTE_IX_NAME], - // m_name)) && - // m_strings.touch(attr1[ATTRIBUTE_IX_NAME], - // m_name) && - (attr1[ATTRIBUTE_IX_NAME] != attr2[ATTRIBUTE_IX_NAME])) { - return (attr1[ATTRIBUTE_IX_NAME] < attr2[ATTRIBUTE_IX_NAME]); - // } else if (attr1[ATTRIBUTE_IX_NAMESPACE_URI] < - // attr2[ATTRIBUTE_IX_NAMESPACE_URI]) { - // return true; - } else { - return false; - } - } - - private void setFirstError(AndrolibException error) { - if (mFirstError == null) { - mFirstError = error; - } - } - - // ///////////////////////////////// data + // ///////////////////////////////// data /* * All values are essentially indices, e.g. m_name is an index of name in * m_strings. */ - private ExtDataInput m_reader; - private ResAttrDecoder mAttrDecoder; - private AndrolibException mFirstError; + private ExtDataInput m_reader; + private ResAttrDecoder mAttrDecoder; + private AndrolibException mFirstError; - private boolean m_operational = false; - private StringBlock m_strings; - private int[] m_resourceIDs; - private NamespaceStack m_namespaces = new NamespaceStack(); - private boolean m_decreaseDepth; - private int m_event; - private int m_lineNumber; - private int m_name; - private int m_namespaceUri; - private int[] m_attributes; - private int m_idAttribute; - private int m_classAttribute; - private int m_styleAttribute; + private boolean m_operational = false; + private StringBlock m_strings; + private int[] m_resourceIDs; + private NamespaceStack m_namespaces = new NamespaceStack(); + private boolean m_decreaseDepth; + private int m_event; + private int m_lineNumber; + private int m_name; + private int m_namespaceUri; + private int[] m_attributes; + private int m_idAttribute; + private int m_classAttribute; + private int m_styleAttribute; - private final static Logger LOGGER = Logger - .getLogger(AXmlResourceParser.class.getName()); - private static final String E_NOT_SUPPORTED = "Method is not supported."; - private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0, - ATTRIBUTE_IX_NAME = 1, ATTRIBUTE_IX_VALUE_STRING = 2, - ATTRIBUTE_IX_VALUE_TYPE = 3, ATTRIBUTE_IX_VALUE_DATA = 4, - ATTRIBUTE_LENGHT = 5; + private final static Logger LOGGER = Logger + .getLogger(AXmlResourceParser.class.getName()); + private static final String E_NOT_SUPPORTED = "Method is not supported."; + private static final int ATTRIBUTE_IX_NAMESPACE_URI = 0, + ATTRIBUTE_IX_NAME = 1, ATTRIBUTE_IX_VALUE_STRING = 2, + ATTRIBUTE_IX_VALUE_TYPE = 3, ATTRIBUTE_IX_VALUE_DATA = 4, + ATTRIBUTE_LENGTH = 5; - private static final int CHUNK_AXML_FILE = 0x00080003, - CHUNK_RESOURCEIDS = 0x00080180, CHUNK_XML_FIRST = 0x00100100, - CHUNK_XML_START_NAMESPACE = 0x00100100, - CHUNK_XML_END_NAMESPACE = 0x00100101, - CHUNK_XML_START_TAG = 0x00100102, CHUNK_XML_END_TAG = 0x00100103, - CHUNK_XML_TEXT = 0x00100104, CHUNK_XML_LAST = 0x00100104; + private static final int CHUNK_AXML_FILE = 0x00080003, + CHUNK_RESOURCEIDS = 0x00080180, CHUNK_XML_FIRST = 0x00100100, + CHUNK_XML_START_NAMESPACE = 0x00100100, + CHUNK_XML_END_NAMESPACE = 0x00100101, + CHUNK_XML_START_TAG = 0x00100102, CHUNK_XML_END_TAG = 0x00100103, + CHUNK_XML_TEXT = 0x00100104, CHUNK_XML_LAST = 0x00100104; } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java index 472f8524..531c90b6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/Res9patchStreamDecoder.java @@ -22,118 +22,121 @@ import brut.util.ExtDataInput; import java.awt.image.BufferedImage; import java.io.*; import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; + import org.apache.commons.io.IOUtils; /** * @author Ryszard Wiśniewski */ public class Res9patchStreamDecoder implements ResStreamDecoder { - @Override - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - byte[] data = IOUtils.toByteArray(in); + @Override + public void decode(InputStream in, OutputStream out) + throws AndrolibException { + try { + byte[] data = IOUtils.toByteArray(in); - BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); - int w = im.getWidth(), h = im.getHeight(); + BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); + int w = im.getWidth(), h = im.getHeight(); - BufferedImage im2 = new BufferedImage(w + 2, h + 2, - BufferedImage.TYPE_4BYTE_ABGR); - if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) { - im2.getRaster().setRect(1, 1, im.getRaster()); - } else { - im2.getGraphics().drawImage(im, 1, 1, null); - } + ImageTypeSpecifier its = ImageTypeSpecifier.createFromRenderedImage( im ); + BufferedImage im2 = its.createBufferedImage( w+2, h+2 ); - NinePatch np = getNinePatch(data); - drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); - drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); + im2.getRaster().setRect(1, 1, im.getRaster()); + NinePatch np = getNinePatch(data); + drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); + drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); - int[] xDivs = np.xDivs; - for (int i = 0; i < xDivs.length; i += 2) { - drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); - } + int[] xDivs = np.xDivs; + for (int i = 0; i < xDivs.length; i += 2) { + drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); + } - int[] yDivs = np.yDivs; - for (int i = 0; i < yDivs.length; i += 2) { - drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); - } + int[] yDivs = np.yDivs; + for (int i = 0; i < yDivs.length; i += 2) { + drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); + } - ImageIO.write(im2, "png", out); - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } + ImageIO.write(im2, "png", out); + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (NullPointerException ex) { + // In my case this was triggered because a .png file was + // containing a html document instead of an image. + // This could be more verbose and try to MIME ? + throw new AndrolibException(ex); + } + } - private NinePatch getNinePatch(byte[] data) throws AndrolibException, - IOException { - ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); - find9patchChunk(di); - return NinePatch.decode(di); - } + private NinePatch getNinePatch(byte[] data) throws AndrolibException, + IOException { + ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); + find9patchChunk(di); + return NinePatch.decode(di); + } - private void find9patchChunk(DataInput di) throws AndrolibException, - IOException { - di.skipBytes(8); - while (true) { - int size; - try { - size = di.readInt(); - } catch (IOException ex) { - throw new CantFind9PatchChunk("Cant find nine patch chunk", ex); - } - if (di.readInt() == NP_CHUNK_TYPE) { - return; - } - di.skipBytes(size + 4); - } - } + private void find9patchChunk(DataInput di) throws AndrolibException, + IOException { + di.skipBytes(8); + while (true) { + int size; + try { + size = di.readInt(); + } catch (IOException ex) { + throw new CantFind9PatchChunk("Cant find nine patch chunk", ex); + } + if (di.readInt() == NP_CHUNK_TYPE) { + return; + } + di.skipBytes(size + 4); + } + } - private void drawHLine(BufferedImage im, int y, int x1, int x2) { - for (int x = x1; x <= x2; x++) { - im.setRGB(x, y, NP_COLOR); - } - } + private void drawHLine(BufferedImage im, int y, int x1, int x2) { + for (int x = x1; x <= x2; x++) { + im.setRGB(x, y, NP_COLOR); + } + } - private void drawVLine(BufferedImage im, int x, int y1, int y2) { - for (int y = y1; y <= y2; y++) { - im.setRGB(x, y, NP_COLOR); - } - } + private void drawVLine(BufferedImage im, int x, int y1, int y2) { + for (int y = y1; y <= y2; y++) { + im.setRGB(x, y, NP_COLOR); + } + } - private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc - private static final int NP_COLOR = 0xff000000; + private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc + private static final int NP_COLOR = 0xff000000; - private static class NinePatch { - public final int padLeft, padRight, padTop, padBottom; - public final int[] xDivs, yDivs; + private static class NinePatch { + public final int padLeft, padRight, padTop, padBottom; + public final int[] xDivs, yDivs; - public NinePatch(int padLeft, int padRight, int padTop, int padBottom, - int[] xDivs, int[] yDivs) { - this.padLeft = padLeft; - this.padRight = padRight; - this.padTop = padTop; - this.padBottom = padBottom; - this.xDivs = xDivs; - this.yDivs = yDivs; - } + public NinePatch(int padLeft, int padRight, int padTop, int padBottom, + int[] xDivs, int[] yDivs) { + this.padLeft = padLeft; + this.padRight = padRight; + this.padTop = padTop; + this.padBottom = padBottom; + this.xDivs = xDivs; + this.yDivs = yDivs; + } - public static NinePatch decode(ExtDataInput di) throws IOException { - di.skipBytes(1); - byte numXDivs = di.readByte(); - byte numYDivs = di.readByte(); - di.skipBytes(1); - di.skipBytes(8); - int padLeft = di.readInt(); - int padRight = di.readInt(); - int padTop = di.readInt(); - int padBottom = di.readInt(); - di.skipBytes(4); - int[] xDivs = di.readIntArray(numXDivs); - int[] yDivs = di.readIntArray(numYDivs); + public static NinePatch decode(ExtDataInput di) throws IOException { + di.skipBytes(1); + byte numXDivs = di.readByte(); + byte numYDivs = di.readByte(); + di.skipBytes(1); + di.skipBytes(8); + int padLeft = di.readInt(); + int padRight = di.readInt(); + int padTop = di.readInt(); + int padBottom = di.readInt(); + di.skipBytes(4); + int[] xDivs = di.readIntArray(numXDivs); + int[] yDivs = di.readIntArray(numYDivs); - return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, - yDivs); - } - } + return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, + yDivs); + } + } } \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java index 8287d613..dda44168 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java @@ -25,31 +25,31 @@ import brut.androlib.res.data.value.ResScalarValue; * @author Ryszard Wiśniewski */ public class ResAttrDecoder { - public String decode(int type, int value, String rawValue, int attrResId) - throws AndrolibException { - ResScalarValue resValue = mCurrentPackage.getValueFactory().factory( - type, value, rawValue); + public String decode(int type, int value, String rawValue, int attrResId) + throws AndrolibException { + ResScalarValue resValue = mCurrentPackage.getValueFactory().factory( + type, value, rawValue); - String decoded = null; - if (attrResId != 0) { - ResAttr attr = (ResAttr) getCurrentPackage().getResTable() - .getResSpec(attrResId).getDefaultResource().getValue(); - decoded = attr.convertToResXmlFormat(resValue); - } + String decoded = null; + if (attrResId != 0) { + ResAttr attr = (ResAttr) getCurrentPackage().getResTable() + .getResSpec(attrResId).getDefaultResource().getValue(); + decoded = attr.convertToResXmlFormat(resValue); + } - return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); - } + return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); + } - public ResPackage getCurrentPackage() throws AndrolibException { - if (mCurrentPackage == null) { - throw new AndrolibException("Current package not set"); - } - return mCurrentPackage; - } + public ResPackage getCurrentPackage() throws AndrolibException { + if (mCurrentPackage == null) { + throw new AndrolibException("Current package not set"); + } + return mCurrentPackage; + } - public void setCurrentPackage(ResPackage currentPackage) { - mCurrentPackage = currentPackage; - } + public void setCurrentPackage(ResPackage currentPackage) { + mCurrentPackage = currentPackage; + } - private ResPackage mCurrentPackage; + private ResPackage mCurrentPackage; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java index 18575630..89f89fd2 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java @@ -31,121 +31,121 @@ import java.util.logging.Logger; * @author Ryszard Wiśniewski */ public class ResFileDecoder { - private final ResStreamDecoderContainer mDecoders; + private final ResStreamDecoderContainer mDecoders; - public ResFileDecoder(ResStreamDecoderContainer decoders) { - this.mDecoders = decoders; - } + public ResFileDecoder(ResStreamDecoderContainer decoders) { + this.mDecoders = decoders; + } - public void decode(ResResource res, Directory inDir, Directory outDir) - throws AndrolibException { + public void decode(ResResource res, Directory inDir, Directory outDir) + throws AndrolibException { - ResFileValue fileValue = (ResFileValue) res.getValue(); - String inFileName = fileValue.getStrippedPath(); - String outResName = res.getFilePath(); - String typeName = res.getResSpec().getType().getName(); + ResFileValue fileValue = (ResFileValue) res.getValue(); + String inFileName = fileValue.getStrippedPath(); + String outResName = res.getFilePath(); + String typeName = res.getResSpec().getType().getName(); - String ext = null; - String outFileName; - int extPos = inFileName.lastIndexOf("."); - if (extPos == -1) { - outFileName = outResName; - } else { - ext = inFileName.substring(extPos); - outFileName = outResName + ext; - } + String ext = null; + String outFileName; + int extPos = inFileName.lastIndexOf("."); + if (extPos == -1) { + outFileName = outResName; + } else { + ext = inFileName.substring(extPos); + outFileName = outResName + ext; + } - try { - if (typeName.equals("raw")) { - decode(inDir, inFileName, outDir, outFileName, "raw"); - return; - } - if (typeName.equals("drawable") || typeName.equals("mipmap")) { - if (inFileName.toLowerCase().endsWith(".9.png")) { - outFileName = outResName + ".9" + ext; + try { + if (typeName.equals("raw")) { + decode(inDir, inFileName, outDir, outFileName, "raw"); + return; + } + if (typeName.equals("drawable") || typeName.equals("mipmap")) { + if (inFileName.toLowerCase().endsWith(".9.png")) { + outFileName = outResName + ".9" + ext; - // check for htc .r.9.png - if (inFileName.toLowerCase().endsWith(".r.9.png")) { - outFileName = outResName + ".r.9" + ext; - } + // check for htc .r.9.png + if (inFileName.toLowerCase().endsWith(".r.9.png")) { + outFileName = outResName + ".r.9" + ext; + } - try { - decode(inDir, inFileName, outDir, outFileName, "9patch"); - return; - } catch (CantFind9PatchChunk ex) { - LOGGER.log( - Level.WARNING, - String.format( - "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", - inFileName), ex); - outDir.removeFile(outFileName); - outFileName = outResName + ext; - } - } - if (!".xml".equals(ext)) { - decode(inDir, inFileName, outDir, outFileName, "raw"); - return; - } - } + try { + decode(inDir, inFileName, outDir, outFileName, "9patch"); + return; + } catch (CantFind9PatchChunk ex) { + LOGGER.log( + Level.WARNING, + String.format( + "Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.", + inFileName), ex); + outDir.removeFile(outFileName); + outFileName = outResName + ext; + } + } + if (!".xml".equals(ext)) { + decode(inDir, inFileName, outDir, outFileName, "raw"); + return; + } + } - decode(inDir, inFileName, outDir, outFileName, "xml"); - } catch (AndrolibException ex) { - LOGGER.log(Level.SEVERE, String.format( - "Could not decode file, replacing by FALSE value: %s", - inFileName, outFileName), ex); - res.replace(new ResBoolValue(false, null)); - } - } + decode(inDir, inFileName, outDir, outFileName, "xml"); + } catch (AndrolibException ex) { + LOGGER.log(Level.SEVERE, String.format( + "Could not decode file, replacing by FALSE value: %s", + inFileName, outFileName), ex); + res.replace(new ResBoolValue(false, null)); + } + } - public void decode(Directory inDir, String inFileName, Directory outDir, - String outFileName, String decoder) throws AndrolibException { - InputStream in = null; - OutputStream out = null; - try { - in = inDir.getFileInput(inFileName); - out = outDir.getFileOutput(outFileName); - mDecoders.decode(in, out, decoder); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - try { - if (in != null) { - in.close(); - } - if (out != null) { - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - } + public void decode(Directory inDir, String inFileName, Directory outDir, + String outFileName, String decoder) throws AndrolibException { + InputStream in = null; + OutputStream out = null; + try { + in = inDir.getFileInput(inFileName); + out = outDir.getFileOutput(outFileName); + mDecoders.decode(in, out, decoder); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } finally { + try { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } + } - public void decodeManifest(Directory inDir, String inFileName, - Directory outDir, String outFileName) throws AndrolibException { - InputStream in = null; - OutputStream out = null; - try { - in = inDir.getFileInput(inFileName); - out = outDir.getFileOutput(outFileName); - ((XmlPullStreamDecoder) mDecoders.getDecoder("xml")) - .decodeManifest(in, out); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } finally { - try { - if (in != null) { - in.close(); - } - if (out != null) { - out.close(); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } - } + public void decodeManifest(Directory inDir, String inFileName, + Directory outDir, String outFileName) throws AndrolibException { + InputStream in = null; + OutputStream out = null; + try { + in = inDir.getFileInput(inFileName); + out = outDir.getFileOutput(outFileName); + ((XmlPullStreamDecoder) mDecoders.getDecoder("xml")) + .decodeManifest(in, out); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } finally { + try { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } + } - private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class - .getName()); + private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class + .getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java index 3ee66657..8c515a2f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResRawStreamDecoder.java @@ -26,13 +26,13 @@ import org.apache.commons.io.IOUtils; * @author Ryszard Wiśniewski */ public class ResRawStreamDecoder implements ResStreamDecoder { - @Override - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - IOUtils.copy(in, out); - } catch (IOException ex) { - throw new AndrolibException("Could not decode raw stream", ex); - } - } + @Override + public void decode(InputStream in, OutputStream out) + throws AndrolibException { + try { + IOUtils.copy(in, out); + } catch (IOException ex) { + throw new AndrolibException("Could not decode raw stream", ex); + } + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java index 2541baaa..012f0254 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java @@ -24,6 +24,6 @@ import java.io.OutputStream; * @author Ryszard Wiśniewski */ public interface ResStreamDecoder { - public void decode(InputStream in, OutputStream out) - throws AndrolibException; + public void decode(InputStream in, OutputStream out) + throws AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java index ce26e5a2..072d1f64 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoderContainer.java @@ -26,22 +26,22 @@ import java.util.Map; * @author Ryszard Wiśniewski */ public class ResStreamDecoderContainer { - private final Map mDecoders = new HashMap(); + private final Map mDecoders = new HashMap(); - public void decode(InputStream in, OutputStream out, String decoderName) - throws AndrolibException { - getDecoder(decoderName).decode(in, out); - } + public void decode(InputStream in, OutputStream out, String decoderName) + throws AndrolibException { + getDecoder(decoderName).decode(in, out); + } - public ResStreamDecoder getDecoder(String name) throws AndrolibException { - ResStreamDecoder decoder = mDecoders.get(name); - if (decoder == null) { - throw new AndrolibException("Undefined decoder: " + name); - } - return decoder; - } + public ResStreamDecoder getDecoder(String name) throws AndrolibException { + ResStreamDecoder decoder = mDecoders.get(name); + if (decoder == null) { + throw new AndrolibException("Undefined decoder: " + name); + } + return decoder; + } - public void setDecoder(String name, ResStreamDecoder decoder) { - mDecoders.put(name, decoder); - } + public void setDecoder(String name, ResStreamDecoder decoder) { + mDecoders.put(name, decoder); + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java index d892e13b..38686d16 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java @@ -21,320 +21,328 @@ import brut.util.ExtDataInput; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.*; +import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; /** * @author Ryszard Wiśniewski * @author Dmitry Skiba - * + * * Block of strings, used in binary xml and arsc. - * + * * TODO: - implement get() - * + * */ public class StringBlock { - /** - * Reads whole (including chunk type) string block from stream. Stream must - * be at the chunk type. - */ - public static StringBlock read(ExtDataInput reader) throws IOException { - reader.skipCheckInt(CHUNK_TYPE); - int chunkSize = reader.readInt(); - int stringCount = reader.readInt(); - int styleOffsetCount = reader.readInt(); - int flags = reader.readInt(); - int stringsOffset = reader.readInt(); - int stylesOffset = reader.readInt(); + /** + * Reads whole (including chunk type) string block from stream. Stream must + * be at the chunk type. + */ + public static StringBlock read(ExtDataInput reader) throws IOException { + reader.skipCheckInt(CHUNK_TYPE); + int chunkSize = reader.readInt(); + int stringCount = reader.readInt(); + int styleOffsetCount = reader.readInt(); + int flags = reader.readInt(); + int stringsOffset = reader.readInt(); + int stylesOffset = reader.readInt(); - StringBlock block = new StringBlock(); - block.m_isUTF8 = (flags & UTF8_FLAG) != 0; - block.m_stringOffsets = reader.readIntArray(stringCount); - block.m_stringOwns = new int[stringCount]; - for (int i = 0; i < stringCount; i++) { - block.m_stringOwns[i] = -1; - } - if (styleOffsetCount != 0) { - block.m_styleOffsets = reader.readIntArray(styleOffsetCount); - } - { - int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - - stringsOffset; - if ((size % 4) != 0) { - throw new IOException("String data size is not multiple of 4 (" - + size + ")."); - } - block.m_strings = new byte[size]; - reader.readFully(block.m_strings); - } - if (stylesOffset != 0) { - int size = (chunkSize - stylesOffset); - if ((size % 4) != 0) { - throw new IOException("Style data size is not multiple of 4 (" - + size + ")."); - } - block.m_styles = reader.readIntArray(size / 4); - } + StringBlock block = new StringBlock(); + block.m_isUTF8 = (flags & UTF8_FLAG) != 0; + block.m_stringOffsets = reader.readIntArray(stringCount); + block.m_stringOwns = new int[stringCount]; + Arrays.fill(block.m_stringOwns, -1); - return block; - } + if (styleOffsetCount != 0) { + block.m_styleOffsets = reader.readIntArray(styleOffsetCount); + } + { + int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) + - stringsOffset; + if ((size % 4) != 0) { + throw new IOException("String data size is not multiple of 4 (" + size + ")."); + } + block.m_strings = new byte[size]; + reader.readFully(block.m_strings); + } + if (stylesOffset != 0) { + int size = (chunkSize - stylesOffset); + if ((size % 4) != 0) { + throw new IOException("Style data size is not multiple of 4 (" + size + ")."); + } + block.m_styles = reader.readIntArray(size / 4); + } - /** - * Returns number of strings in block. - */ - public int getCount() { - return m_stringOffsets != null ? m_stringOffsets.length : 0; - } + return block; + } - /** - * Returns raw string (without any styling information) at specified index. - */ - public String getString(int index) { - if (index < 0 || m_stringOffsets == null - || index >= m_stringOffsets.length) { - return null; - } - int offset = m_stringOffsets[index]; - int length; + /** + * Returns number of strings in block. + */ + public int getCount() { + return m_stringOffsets != null ? m_stringOffsets.length : 0; + } - if (!m_isUTF8) { - length = getShort(m_strings, offset) * 2; - offset += 2; - } else { - offset += getVarint(m_strings, offset)[1]; - int[] varint = getVarint(m_strings, offset); - offset += varint[1]; - length = varint[0]; - } - return decodeString(offset, length); - } + /** + * Returns raw string (without any styling information) at specified index. + */ + public String getString(int index) { + if (index < 0 || m_stringOffsets == null || index >= m_stringOffsets.length) { + return null; + } + int offset = m_stringOffsets[index]; + int length; - /** - * Not yet implemented. - * - * Returns string with style information (if any). - */ - public CharSequence get(int index) { - return getString(index); - } + if (m_isUTF8) { + int[] val = getUtf8(m_strings, offset); + offset = val[0]; + length = val[1]; + } else { + int[] val = getUtf16(m_strings, offset); + offset += val[0]; + length = val[1]; + } + return decodeString(offset, length); + } - /** - * Returns string with style tags (html-like). - */ - public String getHTML(int index) { - String raw = getString(index); - if (raw == null) { - return raw; - } - int[] style = getStyle(index); - if (style == null) { - return ResXmlEncoders.escapeXmlChars(raw); - } - StringBuilder html = new StringBuilder(raw.length() + 32); - int[] opened = new int[style.length / 3]; - int offset = 0, depth = 0; - while (true) { - int i = -1, j; - for (j = 0; j != style.length; j += 3) { - if (style[j + 1] == -1) { - continue; - } - if (i == -1 || style[i + 1] > style[j + 1]) { - i = j; - } - } - int start = ((i != -1) ? style[i + 1] : raw.length()); - for (j = depth - 1; j >= 0; j--) { - int last = opened[j]; - int end = style[last + 2]; - if (end >= start) { - break; - } - if (offset <= end) { - html.append(ResXmlEncoders.escapeXmlChars(raw.substring( - offset, end + 1))); - offset = end + 1; - } - outputStyleTag(getString(style[last]), html, true); - } - depth = j + 1; - if (offset < start) { - html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset, - start))); - offset = start; - } - if (i == -1) { - break; - } - outputStyleTag(getString(style[i]), html, false); - style[i + 1] = -1; - opened[depth++] = i; - } - return html.toString(); - } + /** + * Not yet implemented. + * + * Returns string with style information (if any). + */ + public CharSequence get(int index) { + return getString(index); + } - private void outputStyleTag(String tag, StringBuilder builder, boolean close) { - builder.append('<'); - if (close) { - builder.append('/'); - } + /** + * Returns string with style tags (html-like). + */ + public String getHTML(int index) { + String raw = getString(index); + if (raw == null) { + return raw; + } + int[] style = getStyle(index); + if (style == null) { + return ResXmlEncoders.escapeXmlChars(raw); + } + StringBuilder html = new StringBuilder(raw.length() + 32); + int[] opened = new int[style.length / 3]; + int offset = 0, depth = 0; + while (true) { + int i = -1, j; + for (j = 0; j != style.length; j += 3) { + if (style[j + 1] == -1) { + continue; + } + if (i == -1 || style[i + 1] > style[j + 1]) { + i = j; + } + } + int start = ((i != -1) ? style[i + 1] : raw.length()); + for (j = depth - 1; j >= 0; j--) { + int last = opened[j]; + int end = style[last + 2]; + if (end >= start) { + break; + } + if (offset <= end) { + html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset, end + 1))); + offset = end + 1; + } + outputStyleTag(getString(style[last]), html, true); + } + depth = j + 1; + if (offset < start) { + html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset, start))); + offset = start; + } + if (i == -1) { + break; + } + outputStyleTag(getString(style[i]), html, false); + style[i + 1] = -1; + opened[depth++] = i; + } + return html.toString(); + } - int pos = tag.indexOf(';'); - if (pos == -1) { - builder.append(tag); - } else { - builder.append(tag.substring(0, pos)); - if (!close) { - boolean loop = true; - while (loop) { - int pos2 = tag.indexOf('=', pos + 1); - builder.append(' ').append(tag.substring(pos + 1, pos2)) - .append("=\""); - pos = tag.indexOf(';', pos2 + 1); + private void outputStyleTag(String tag, StringBuilder builder, boolean close) { + builder.append('<'); + if (close) { + builder.append('/'); + } - String val; - if (pos != -1) { - val = tag.substring(pos2 + 1, pos); - } else { - loop = false; - val = tag.substring(pos2 + 1); - } + int pos = tag.indexOf(';'); + if (pos == -1) { + builder.append(tag); + } else { + builder.append(tag.substring(0, pos)); + if (!close) { + boolean loop = true; + while (loop) { + int pos2 = tag.indexOf('=', pos + 1); - builder.append(ResXmlEncoders.escapeXmlChars(val)).append( - '"'); - } - } - } - builder.append('>'); - } + // malformed style information will cause crash. so + // prematurely end style tags, if recreation + // cannot be created. + if (pos2 != -1) { + builder.append(' ').append(tag.substring(pos + 1, pos2)).append("=\""); + pos = tag.indexOf(';', pos2 + 1); - /** - * Finds index of the string. Returns -1 if the string was not found. - */ - public int find(String string) { - if (string == null) { - return -1; - } - for (int i = 0; i != m_stringOffsets.length; ++i) { - int offset = m_stringOffsets[i]; - int length = getShort(m_strings, offset); - if (length != string.length()) { - continue; - } - int j = 0; - for (; j != length; ++j) { - offset += 2; - if (string.charAt(j) != getShort(m_strings, offset)) { - break; - } - } - if (j == length) { - return i; - } - } - return -1; - } + String val; + if (pos != -1) { + val = tag.substring(pos2 + 1, pos); + } else { + loop = false; + val = tag.substring(pos2 + 1); + } - // /////////////////////////////////////////// implementation - private StringBlock() { - } + builder.append(ResXmlEncoders.escapeXmlChars(val)).append('"'); + } else { + loop = false; + } - /** - * Returns style information - array of int triplets, where in each triplet: - * * first int is index of tag name ('b','i', etc.) * second int is tag - * start index in string * third int is tag end index in string - */ - private int[] getStyle(int index) { - if (m_styleOffsets == null || m_styles == null - || index >= m_styleOffsets.length) { - return null; - } - int offset = m_styleOffsets[index] / 4; - int style[]; - { - int count = 0; - for (int i = offset; i < m_styles.length; ++i) { - if (m_styles[i] == -1) { - break; - } - count += 1; - } - if (count == 0 || (count % 3) != 0) { - return null; - } - style = new int[count]; - } - for (int i = offset, j = 0; i < m_styles.length;) { - if (m_styles[i] == -1) { - break; - } - style[j++] = m_styles[i++]; - } - return style; - } + } + } + } + builder.append('>'); + } - private String decodeString(int offset, int length) { - try { - return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode( - ByteBuffer.wrap(m_strings, offset, length)).toString(); - } catch (CharacterCodingException ex) { - LOGGER.log(Level.WARNING, null, ex); - return null; - } - } + /** + * Finds index of the string. Returns -1 if the string was not found. + */ + public int find(String string) { + if (string == null) { + return -1; + } + for (int i = 0; i != m_stringOffsets.length; ++i) { + int offset = m_stringOffsets[i]; + int length = getShort(m_strings, offset); + if (length != string.length()) { + continue; + } + int j = 0; + for (; j != length; ++j) { + offset += 2; + if (string.charAt(j) != getShort(m_strings, offset)) { + break; + } + } + if (j == length) { + return i; + } + } + return -1; + } - private static final int getShort(byte[] array, int offset) { - return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff; - } + // /////////////////////////////////////////// implementation + private StringBlock() { + } - private static final int getShort(int[] array, int offset) { - int value = array[offset / 4]; - if ((offset % 4) / 2 == 0) { - return (value & 0xFFFF); - } else { - return (value >>> 16); - } - } + /** + * Returns style information - array of int triplets, where in each triplet: + * * first int is index of tag name ('b','i', etc.) * second int is tag + * start index in string * third int is tag end index in string + */ + private int[] getStyle(int index) { + if (m_styleOffsets == null || m_styles == null|| index >= m_styleOffsets.length) { + return null; + } + int offset = m_styleOffsets[index] / 4; + int style[]; + { + int count = 0; + for (int i = offset; i < m_styles.length; ++i) { + if (m_styles[i] == -1) { + break; + } + count += 1; + } + if (count == 0 || (count % 3) != 0) { + return null; + } + style = new int[count]; + } + for (int i = offset, j = 0; i < m_styles.length;) { + if (m_styles[i] == -1) { + break; + } + style[j++] = m_styles[i++]; + } + return style; + } - private static final int[] getVarint(byte[] array, int offset) { - int val = array[offset]; - boolean more = (val & 0x80) != 0; - val &= 0x7f; + private String decodeString(int offset, int length) { + try { + return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode( + ByteBuffer.wrap(m_strings, offset, length)).toString(); + } catch (CharacterCodingException ex) { + LOGGER.log(Level.WARNING, null, ex); + return null; + } + } - if (!more) { - return new int[] { val, 1 }; - } else { - return new int[] { val << 8 | array[offset + 1] & 0xff, 2 }; - } - } + private static final int getShort(byte[] array, int offset) { + return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff; + } - public boolean touch(int index, int own) { - if (index < 0 || m_stringOwns == null || index >= m_stringOwns.length) { - return false; - } - if (m_stringOwns[index] == -1) { - m_stringOwns[index] = own; - return true; - } else if (m_stringOwns[index] == own) { - return true; - } else { - return false; - } - } + private static final int getShort(int[] array, int offset) { + int value = array[offset / 4]; + if ((offset % 4) / 2 == 0) { + return (value & 0xFFFF); + } else { + return (value >>> 16); + } + } - private int[] m_stringOffsets; - private byte[] m_strings; - private int[] m_styleOffsets; - private int[] m_styles; - private boolean m_isUTF8; - private int[] m_stringOwns; - private static final CharsetDecoder UTF16LE_DECODER = Charset.forName( - "UTF-16LE").newDecoder(); - private static final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8") - .newDecoder(); - private static final Logger LOGGER = Logger.getLogger(StringBlock.class - .getName()); - private static final int CHUNK_TYPE = 0x001C0001; - private static final int UTF8_FLAG = 0x00000100; + private static final int[] getUtf8(byte[] array, int offset) { + int val = array[offset]; + int length; + + if ((val & 0x80) != 0) { + offset += 2; + } else { + offset += 1; + } + val = array[offset]; + if ((val & 0x80) != 0) { + offset += 2; + } else { + offset += 1; + } + length = 0; + while (array[offset + length] != 0) { + length++; + } + return new int[] { offset, length}; + } + + private static final int[] getUtf16(byte[] array, int offset) { + int val = ((array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF); + + if (val == 0x8000) { + int high = (array[offset + 3] & 0xFF) << 8; + int low = (array[offset + 2] & 0xFF); + return new int[] {4, (high + low) * 2}; + } + return new int[] {2, val * 2}; + } + + private int[] m_stringOffsets; + private byte[] m_strings; + private int[] m_styleOffsets; + private int[] m_styles; + private boolean m_isUTF8; + private int[] m_stringOwns; + + private final CharsetDecoder UTF16LE_DECODER = Charset.forName("UTF-16LE").newDecoder(); + private final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8").newDecoder(); + private static final Logger LOGGER = Logger.getLogger(StringBlock.class.getName()); + + // ResChunk_header = header.type (0x0001) + header.headerSize (0x001C) + private static final int CHUNK_TYPE = 0x001C0001; + private static final int UTF8_FLAG = 0x00000100; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java index 89643fc3..4234cf20 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java @@ -36,133 +36,138 @@ import brut.androlib.res.util.ExtXmlSerializer; * @author Ryszard Wiśniewski */ public class XmlPullStreamDecoder implements ResStreamDecoder { - public XmlPullStreamDecoder(XmlPullParser parser, - ExtXmlSerializer serializer) { - this.mParser = parser; - this.mSerial = serializer; - } + public XmlPullStreamDecoder(XmlPullParser parser, + ExtXmlSerializer serializer) { + this.mParser = parser; + this.mSerial = serializer; + } - @Override - public void decode(InputStream in, OutputStream out) - throws AndrolibException { - try { - XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); - XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); - final ResTable resTable = ((AXmlResourceParser) mParser) - .getAttrDecoder().getCurrentPackage().getResTable(); + @Override + public void decode(InputStream in, OutputStream out) + throws AndrolibException { + try { + XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); + XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); + final ResTable resTable = ((AXmlResourceParser) mParser) + .getAttrDecoder().getCurrentPackage().getResTable(); - XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, - factory) { - boolean hideSdkInfo = false; - boolean hidePackageInfo = false; + XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, + factory) { + boolean hideSdkInfo = false; + boolean hidePackageInfo = false; - @Override - public void event(XmlPullParser pp) - throws XmlPullParserException, IOException { - int type = pp.getEventType(); + @Override + public void event(XmlPullParser pp) + throws XmlPullParserException, IOException { + int type = pp.getEventType(); - if (type == XmlPullParser.START_TAG) { - if ("manifest".equalsIgnoreCase(pp.getName())) { - try { - hidePackageInfo = parseManifest(pp); - } catch (AndrolibException e) { - } - } else if ("uses-sdk".equalsIgnoreCase(pp.getName())) { - try { - hideSdkInfo = parseAttr(pp); - if (hideSdkInfo) { - return; - } - } catch (AndrolibException e) { - } - } - } else if (hideSdkInfo && type == XmlPullParser.END_TAG - && "uses-sdk".equalsIgnoreCase(pp.getName())) { - return; - } else if (hidePackageInfo && type == XmlPullParser.END_TAG - && "manifest".equalsIgnoreCase(pp.getName())) { - super.event(pp); - return; - } - super.event(pp); - } + if (type == XmlPullParser.START_TAG) { + if ("manifest".equalsIgnoreCase(pp.getName())) { + try { + hidePackageInfo = parseManifest(pp); + } catch (AndrolibException e) { + } + } else if ("uses-sdk".equalsIgnoreCase(pp.getName())) { + try { + hideSdkInfo = parseAttr(pp); + if (hideSdkInfo) { + return; + } + } catch (AndrolibException e) { + } + } + } else if (hideSdkInfo && type == XmlPullParser.END_TAG + && "uses-sdk".equalsIgnoreCase(pp.getName())) { + return; + } else if (hidePackageInfo && type == XmlPullParser.END_TAG + && "manifest".equalsIgnoreCase(pp.getName())) { + super.event(pp); + return; + } + super.event(pp); + } - private boolean parseManifest(XmlPullParser pp) - throws AndrolibException { - ResTable restable = resTable; + private boolean parseManifest(XmlPullParser pp) + throws AndrolibException { - // read for package: - for (int i = 0; i < pp.getAttributeCount(); i++) { - if (pp.getAttributeName(i) - .equalsIgnoreCase(("package"))) { - restable.addPackageInfo("orig_package", - pp.getAttributeValue(i)); - } - } - return true; - } + // read for package: + for (int i = 0; i < pp.getAttributeCount(); i++) { + if (pp.getAttributeName(i).equalsIgnoreCase(("package"))) { + resTable.setPackageRenamed(pp.getAttributeValue(i)); + } else if (pp.getAttributeName(i).equalsIgnoreCase("versionCode")) { + resTable.addVersionInfo("versionCode", pp.getAttributeValue(i)); + } else if (pp.getAttributeName(i).equalsIgnoreCase("versionName")) { + resTable.addVersionInfo("versionName", pp.getAttributeValue(i)); + } + } + return true; + } - private boolean parseAttr(XmlPullParser pp) - throws AndrolibException { - ResTable restable = resTable; - for (int i = 0; i < pp.getAttributeCount(); i++) { - final String a_ns = "http://schemas.android.com/apk/res/android"; - String ns = pp.getAttributeNamespace(i); + private boolean parseAttr(XmlPullParser pp) + throws AndrolibException { + ResTable restable = resTable; + for (int i = 0; i < pp.getAttributeCount(); i++) { + final String a_ns = "http://schemas.android.com/apk/res/android"; + String ns = pp.getAttributeNamespace(i); - if (a_ns.equalsIgnoreCase(ns)) { - String name = pp.getAttributeName(i); - String value = pp.getAttributeValue(i); - if (name != null && value != null) { - if (name.equalsIgnoreCase("minSdkVersion") - || name.equalsIgnoreCase("targetSdkVersion") - || name.equalsIgnoreCase("maxSdkVersion")) { - restable.addSdkInfo(name, value); - } else { - restable.clearSdkInfo(); - return false;// Found unknown flags - } - } - } else { - resTable.clearSdkInfo(); + if (a_ns.equalsIgnoreCase(ns)) { + String name = pp.getAttributeName(i); + String value = pp.getAttributeValue(i); + if (name != null && value != null) { + if (name.equalsIgnoreCase("minSdkVersion") + || name.equalsIgnoreCase("targetSdkVersion") + || name.equalsIgnoreCase("maxSdkVersion")) { + restable.addSdkInfo(name, value); + } else { + restable.clearSdkInfo(); + return false;// Found unknown flags + } + } + } else { + resTable.clearSdkInfo(); - if (i >= pp.getAttributeCount()) { - return false;// Found unknown flags - } - } - } - return true; - } - }; + if (i >= pp.getAttributeCount()) { + return false;// Found unknown flags + } + } + } + if (resTable.getAnalysisMode() == true) { + return false; + } else { + return true; + } + } + }; - par.setInput(in, null); - ser.setOutput(out, null); + par.setInput(in, null); + ser.setOutput(out, null); - while (par.nextToken() != XmlPullParser.END_DOCUMENT) { - ser.event(par); - } - ser.flush(); - } catch (XmlPullParserException ex) { - throw new AndrolibException("Could not decode XML", ex); - } catch (IOException ex) { - throw new AndrolibException("Could not decode XML", ex); - } - } + while (par.nextToken() != XmlPullParser.END_DOCUMENT) { + ser.event(par); + } + ser.flush(); + } catch (XmlPullParserException ex) { + throw new AndrolibException("Could not decode XML", ex); + } catch (IOException ex) { + throw new AndrolibException("Could not decode XML", ex); + } + } - public void decodeManifest(InputStream in, OutputStream out) - throws AndrolibException { - mOptimizeForManifest = true; - try { - decode(in, out); - } finally { - mOptimizeForManifest = false; - } - } + public void decodeManifest(InputStream in, OutputStream out) + throws AndrolibException { + mOptimizeForManifest = true; + try { + decode(in, out); + } finally { + mOptimizeForManifest = false; + } + } - private final XmlPullParser mParser; - private final ExtXmlSerializer mSerial; + private final XmlPullParser mParser; + private final ExtXmlSerializer mSerial; - private boolean mOptimizeForManifest = false; + private boolean mOptimizeForManifest = false; - private final static Logger LOGGER = Logger - .getLogger(XmlPullStreamDecoder.class.getName()); + private final static Logger LOGGER = Logger + .getLogger(XmlPullStreamDecoder.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java index 0532c524..e7e45e64 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtFile.java @@ -27,36 +27,36 @@ import java.net.URI; * @author Ryszard Wiśniewski */ public class ExtFile extends File { - public ExtFile(File file) { - super(file.getPath()); - } + public ExtFile(File file) { + super(file.getPath()); + } - public ExtFile(URI uri) { - super(uri); - } + public ExtFile(URI uri) { + super(uri); + } - public ExtFile(File parent, String child) { - super(parent, child); - } + public ExtFile(File parent, String child) { + super(parent, child); + } - public ExtFile(String parent, String child) { - super(parent, child); - } + public ExtFile(String parent, String child) { + super(parent, child); + } - public ExtFile(String pathname) { - super(pathname); - } + public ExtFile(String pathname) { + super(pathname); + } - public Directory getDirectory() throws DirectoryException { - if (mDirectory == null) { - if (isDirectory()) { - mDirectory = new FileDirectory(this); - } else { - mDirectory = new ZipRODirectory(this); - } - } - return mDirectory; - } + public Directory getDirectory() throws DirectoryException { + if (mDirectory == null) { + if (isDirectory()) { + mDirectory = new FileDirectory(this); + } else { + mDirectory = new ZipRODirectory(this); + } + } + return mDirectory; + } - private Directory mDirectory; + private Directory mDirectory; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java index b776919b..ad422c62 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtMXSerializer.java @@ -23,59 +23,59 @@ import org.xmlpull.mxp1_serializer.MXSerializer; * @author Ryszard Wiśniewski */ public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer { - @Override - public void startDocument(String encoding, Boolean standalone) - throws IOException, IllegalArgumentException, IllegalStateException { - super.startDocument(encoding != null ? encoding : mDefaultEncoding, - standalone); - this.newLine(); - } + @Override + public void startDocument(String encoding, Boolean standalone) + throws IOException, IllegalArgumentException, IllegalStateException { + super.startDocument(encoding != null ? encoding : mDefaultEncoding, + standalone); + this.newLine(); + } - @Override - protected void writeAttributeValue(String value, Writer out) - throws IOException { - if (mIsDisabledAttrEscape) { - out.write(value); - return; - } - super.writeAttributeValue(value, out); - } + @Override + protected void writeAttributeValue(String value, Writer out) + throws IOException { + if (mIsDisabledAttrEscape) { + out.write(value); + return; + } + super.writeAttributeValue(value, out); + } - @Override - public void setOutput(OutputStream os, String encoding) throws IOException { - super.setOutput(os, encoding != null ? encoding : mDefaultEncoding); - } + @Override + public void setOutput(OutputStream os, String encoding) throws IOException { + super.setOutput(os, encoding != null ? encoding : mDefaultEncoding); + } - @Override - public Object getProperty(String name) throws IllegalArgumentException { - if (PROPERTY_DEFAULT_ENCODING.equals(name)) { - return mDefaultEncoding; - } - return super.getProperty(name); - } + @Override + public Object getProperty(String name) throws IllegalArgumentException { + if (PROPERTY_DEFAULT_ENCODING.equals(name)) { + return mDefaultEncoding; + } + return super.getProperty(name); + } - @Override - public void setProperty(String name, Object value) - throws IllegalArgumentException, IllegalStateException { - if (PROPERTY_DEFAULT_ENCODING.equals(name)) { - mDefaultEncoding = (String) value; - } else { - super.setProperty(name, value); - } - } + @Override + public void setProperty(String name, Object value) + throws IllegalArgumentException, IllegalStateException { + if (PROPERTY_DEFAULT_ENCODING.equals(name)) { + mDefaultEncoding = (String) value; + } else { + super.setProperty(name, value); + } + } - @Override - public ExtXmlSerializer newLine() throws IOException { - super.out.write(lineSeparator); - return this; - } + @Override + public ExtXmlSerializer newLine() throws IOException { + super.out.write(lineSeparator); + return this; + } - @Override - public void setDisabledAttrEscape(boolean disabled) { - mIsDisabledAttrEscape = disabled; - } + @Override + public void setDisabledAttrEscape(boolean disabled) { + mIsDisabledAttrEscape = disabled; + } - private String mDefaultEncoding; - private boolean mIsDisabledAttrEscape = false; + private String mDefaultEncoding; + private boolean mIsDisabledAttrEscape = false; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java index 0fdeb9b5..1fdee01c 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/util/ExtXmlSerializer.java @@ -24,11 +24,11 @@ import org.xmlpull.v1.XmlSerializer; */ public interface ExtXmlSerializer extends XmlSerializer { - public ExtXmlSerializer newLine() throws IOException; + public ExtXmlSerializer newLine() throws IOException; - public void setDisabledAttrEscape(boolean disabled); + public void setDisabledAttrEscape(boolean disabled); - public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; - public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; - public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING"; + public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation"; + public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator"; + public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING"; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java index 39764c20..aca8e8a3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResValuesXmlSerializable.java @@ -25,6 +25,6 @@ import org.xmlpull.v1.XmlSerializer; * @author Ryszard Wiśniewski */ public interface ResValuesXmlSerializable { - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException; + public void serializeToResValuesXml(XmlSerializer serializer, + ResResource res) throws IOException, AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java index 6d26fe22..fe7ce628 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncodable.java @@ -22,7 +22,7 @@ import brut.androlib.AndrolibException; * @author Ryszard Wiśniewski */ public interface ResXmlEncodable { - public String encodeAsResXmlAttr() throws AndrolibException; + public String encodeAsResXmlAttr() throws AndrolibException; - public String encodeAsResXmlValue() throws AndrolibException; + public String encodeAsResXmlValue() throws AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java index ea46364f..4eae8e1f 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlEncoders.java @@ -16,6 +16,8 @@ package brut.androlib.res.xml; +import brut.util.Duo; + import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; @@ -25,176 +27,187 @@ import java.util.List; */ public final class ResXmlEncoders { - public static String escapeXmlChars(String str) { - return str.replace("&", "&").replace("<", "<"); - } + public static String escapeXmlChars(String str) { + return str.replace("&", "&").replace("<", "<"); + } - public static String encodeAsResXmlAttr(String str) { - if (str.isEmpty()) { - return str; - } + public static String encodeAsResXmlAttr(String str) { + if (str.isEmpty()) { + return str; + } - char[] chars = str.toCharArray(); - StringBuilder out = new StringBuilder(str.length() + 10); + char[] chars = str.toCharArray(); + StringBuilder out = new StringBuilder(str.length() + 10); - switch (chars[0]) { - case '#': - case '@': - case '?': - out.append('\\'); - } + switch (chars[0]) { + case '#': + case '@': + case '?': + out.append('\\'); + } - for (char c : chars) { - switch (c) { - case '\\': - out.append('\\'); - break; - case '"': - out.append("""); - continue; - case '\n': - out.append("\\n"); - continue; - default: - if (!isPrintableChar(c)) { - out.append(String.format("\\u%04x", (int) c)); - continue; - } - } - out.append(c); - } + for (char c : chars) { + switch (c) { + case '\\': + out.append('\\'); + break; + case '"': + out.append("""); + continue; + case '\n': + out.append("\\n"); + continue; + default: + if (!isPrintableChar(c)) { + out.append(String.format("\\u%04x", (int) c)); + continue; + } + } + out.append(c); + } - return out.toString(); - } + return out.toString(); + } - public static String encodeAsXmlValue(String str) { - if (str.isEmpty()) { - return str; - } + public static String encodeAsXmlValue(String str) { + if (str.isEmpty()) { + return str; + } - char[] chars = str.toCharArray(); - StringBuilder out = new StringBuilder(str.length() + 10); + char[] chars = str.toCharArray(); + StringBuilder out = new StringBuilder(str.length() + 10); - switch (chars[0]) { - case '#': - case '@': - case '?': - out.append('\\'); - } + switch (chars[0]) { + case '#': + case '@': + case '?': + out.append('\\'); + } - boolean isInStyleTag = false; - int startPos = 0; - boolean enclose = false; - boolean wasSpace = true; - for (char c : chars) { - if (isInStyleTag) { - if (c == '>') { - isInStyleTag = false; - startPos = out.length() + 1; - enclose = false; - } - } else if (c == ' ') { - if (wasSpace) { - enclose = true; - } - wasSpace = true; - } else { - wasSpace = false; - switch (c) { - case '\\': - out.append('\\'); - break; - case '\'': - case '\n': - enclose = true; - break; - case '"': - out.append('\\'); - break; - case '<': - isInStyleTag = true; - if (enclose) { - out.insert(startPos, '"').append('"'); - } - break; - default: - if (!isPrintableChar(c)) { - out.append(String.format("\\u%04x", (int) c)); - continue; - } - } - } - out.append(c); - } + boolean isInStyleTag = false; + int startPos = 0; + boolean enclose = false; + boolean wasSpace = true; + for (char c : chars) { + if (isInStyleTag) { + if (c == '>') { + isInStyleTag = false; + startPos = out.length() + 1; + enclose = false; + } + } else if (c == ' ') { + if (wasSpace) { + enclose = true; + } + wasSpace = true; + } else { + wasSpace = false; + switch (c) { + case '\\': + out.append('\\'); + break; + case '\'': + case '\n': + enclose = true; + break; + case '"': + out.append('\\'); + break; + case '<': + isInStyleTag = true; + if (enclose) { + out.insert(startPos, '"').append('"'); + } + break; + default: + if (!isPrintableChar(c)) { - if (enclose || wasSpace) { - out.insert(startPos, '"').append('"'); - } + // lets not write trailing \u0000 if we are at end of string + if ((out.length() + 1) == str.length() && c == '\u0000') { + continue; + } + out.append(String.format("\\u%04x", (int) c)); + continue; + } + } + } + out.append(c); + } - return out.toString(); - } + if (enclose || wasSpace) { + out.insert(startPos, '"').append('"'); + } + return out.toString(); + } - public static boolean hasMultipleNonPositionalSubstitutions(String str) { - return findNonPositionalSubstitutions(str, 2).size() > 1; - } + public static boolean hasMultipleNonPositionalSubstitutions(String str) { + Duo, List> tuple = findSubstitutions(str, 2); + return ! tuple.m1.isEmpty() && tuple.m1.size() + tuple.m2.size() > 1; + } - public static String enumerateNonPositionalSubstitutions(String str) { - List subs = findNonPositionalSubstitutions(str, -1); - if (subs.size() < 2) { - return str; - } + public static String enumerateNonPositionalSubstitutionsIfRequired(String str) { + Duo, List> tuple = findSubstitutions(str, 2); + if (tuple.m1.isEmpty() || tuple.m1.size() + tuple.m2.size() < 2) { + return str; + } + List subs = tuple.m1; - StringBuilder out = new StringBuilder(); - int pos = 0; - int count = 0; - for (Integer sub : subs) { - out.append(str.substring(pos, ++sub)).append(++count).append('$'); - pos = sub; - } - out.append(str.substring(pos)); + StringBuilder out = new StringBuilder(); + int pos = 0; + int count = 0; + for (Integer sub : subs) { + out.append(str.substring(pos, ++sub)).append(++count).append('$'); + pos = sub; + } + out.append(str.substring(pos)); - return out.toString(); - } + return out.toString(); + } - /** - * It searches for "%", but not "%%" nor "%(\d)+\$" - */ - private static List findNonPositionalSubstitutions(String str, - int max) { - int pos = 0; - int pos2 = 0; - int count = 0; - int length = str.length(); - List ret = new ArrayList(); - while ((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) { - if (pos2 == length) { - break; - } - char c = str.charAt(pos2++); - if (c == '%') { - continue; - } - if (c >= '0' && c <= '9' && pos2 < length) { - do { - c = str.charAt(pos2++); - } while (c >= '0' && c <= '9' && pos2 < length); - if (c == '$') { - continue; - } - } + /** + * It returns a tuple of: + * - a list of offsets of non positional substitutions. non-pos is defined as any "%" which isn't "%%" nor "%\d+\$" + * - a list of offsets of positional substitutions + */ + private static Duo, List> findSubstitutions(String str, int nonPosMax) { + if (nonPosMax == -1) { + nonPosMax = Integer.MAX_VALUE; + } + int pos; + int pos2 = 0; + int length = str.length(); + List nonPositional = new ArrayList<>(); + List positional = new ArrayList<>(); + while ((pos = str.indexOf('%', pos2)) != -1) { + pos2 = pos + 1; + if (pos2 == length) { + nonPositional.add(pos); + break; + } + char c = str.charAt(pos2++); + if (c == '%') { + continue; + } + if (c >= '0' && c <= '9' && pos2 < length) { + while ((c = str.charAt(pos2++)) >= '0' && c <= '9' && pos2 < length); + if (c == '$') { + positional.add(pos); + continue; + } + } - ret.add(pos); - if (max != -1 && ++count >= max) { - break; - } - } + nonPositional.add(pos); + if (nonPositional.size() >= nonPosMax) { + break; + } + } - return ret; - } + return new Duo<>(nonPositional, positional); + } - private static boolean isPrintableChar(char c) { - Character.UnicodeBlock block = Character.UnicodeBlock.of(c); - return !Character.isISOControl(c) && c != KeyEvent.CHAR_UNDEFINED - && block != null && block != Character.UnicodeBlock.SPECIALS; - } + private static boolean isPrintableChar(char c) { + Character.UnicodeBlock block = Character.UnicodeBlock.of(c); + return !Character.isISOControl(c) && c != KeyEvent.CHAR_UNDEFINED + && block != null && block != Character.UnicodeBlock.SPECIALS; + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java index 75fe7284..8f20f4a3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DebugInjector.java @@ -20,198 +20,225 @@ import brut.androlib.AndrolibException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.jf.dexlib.Code.Analysis.RegisterType; /** * @author Ryszard Wiśniewski */ public class DebugInjector { - public static void inject(ListIterator it, StringBuilder out) - throws AndrolibException { - new DebugInjector(it, out).inject(); - } + private boolean areParamsInjected; + private int currParam; + private int lastParam; - private DebugInjector(ListIterator it, StringBuilder out) { - mIt = it; - mOut = out; - } + public static void inject(ListIterator it, StringBuilder out) + throws AndrolibException { + new DebugInjector(it, out).inject(); + } - private void inject() throws AndrolibException { - String definition = nextAndAppend(); - if (definition.contains(" abstract ") - || definition.contains(" native ")) { - nextAndAppend(); - return; - } - injectParameters(definition); + private DebugInjector(ListIterator it, StringBuilder out) { + mIt = it; + mOut = out; + } - boolean end = false; - while (!end) { - end = step(); - } - } + private void inject() throws AndrolibException { + String definition = nextAndAppend(); + if (definition.contains(" abstract ") + || definition.contains(" native ")) { + nextAndAppend(); + return; + } + parseParamsNumber(definition); - private void injectParameters(String definition) throws AndrolibException { - int pos = definition.indexOf('('); - if (pos == -1) { - throw new AndrolibException(); - } - int pos2 = definition.indexOf(')', pos); - if (pos2 == -1) { - throw new AndrolibException(); - } - String params = definition.substring(pos + 1, pos2); + boolean end = false; + while (!end) { + end = step(); + } + } - int i = definition.contains(" static ") ? 0 : 1; - int argc = TypeName.listFromInternalName(params).size() + i; - while (i < argc) { - mOut.append(".parameter \"p").append(i).append("\"\n"); - i++; - } - } + private void parseParamsNumber(String definition) throws AndrolibException { + int pos = definition.indexOf('('); + if (pos == -1) { + throw new AndrolibException(); + } + int pos2 = definition.indexOf(')', pos); + if (pos2 == -1) { + throw new AndrolibException(); + } + String params = definition.substring(pos + 1, pos2); - private boolean step() { - String line = next(); - if (line.isEmpty()) { - return false; - } + currParam = definition.contains(" static ") ? 0 : 1; + lastParam = TypeName.listFromInternalName(params).size() + currParam - 1; + } - switch (line.charAt(0)) { - case '#': - return processComment(line); - case ':': - append(line); - return false; - case '.': - return processDirective(line); - default: - return processInstruction(line); - } - } + private void injectRemainingParams() { + areParamsInjected = true; + while(currParam <= lastParam) { + //mOut.append(".param \"p").append(currParam).append("\"\n"); + currParam++; + } + } - private boolean processComment(String line) { - if (mFirstInstruction) { - return false; - } + private boolean step() { + String line = next(); + if (line.isEmpty()) { + return false; + } - Matcher m = REGISTER_INFO_PATTERN.matcher(line); + switch (line.charAt(0)) { + case '#': + return processComment(line); + case ':': + append(line); + return false; + case '.': + return processDirective(line); + default: + if (! areParamsInjected) { + injectRemainingParams(); + } + return processInstruction(line); + } + } - while (m.find()) { - String localName = m.group(1); - String localType = null; - switch (RegisterType.Category.valueOf(m.group(2))) { - case Reference: - case Null: - case UninitRef: - case UninitThis: - localType = "Ljava/lang/Object;"; - break; - case Boolean: - localType = "Z"; - break; - case Integer: - case One: - case Unknown: - localType = "I"; - break; - case Uninit: - case Conflicted: - if (mInitializedRegisters.remove(localName)) { - mOut.append(".end local ").append(localName).append('\n'); - } - continue; - case Short: - case PosShort: - localType = "S"; - break; - case Byte: - case PosByte: - localType = "B"; - break; - case Char: - localType = "C"; - break; - case Float: - localType = "F"; - break; - case LongHi: - case LongLo: - localType = "J"; - break; - case DoubleHi: - case DoubleLo: - localType = "D"; - break; - default: - assert false; - } + private boolean processComment(String line) { + if (mFirstInstruction) { + return false; + } - mInitializedRegisters.add(localName); - mOut.append(".local ").append(localName).append(", ") - .append(localName).append(':').append(localType) - .append('\n'); - } + Matcher m = REGISTER_INFO_PATTERN.matcher(line); - return false; - } + while (m.find()) { + String localName = m.group(1); + String localType = null; + switch (m.group(2)) { + case "Reference": + case "UninitRef": + case "REFERENCE": + case "Null": + case "UninitThis": + localType = "Ljava/lang/Object;"; + break; + case "Boolean": + localType = "Z"; + break; + case "Integer": + case "One": + case "Unknown": + localType = "I"; + break; + case "Uninit": + case "Conflicted": + if (mInitializedRegisters.remove(localName)) { + mOut.append(".end local ").append(localName).append('\n'); + } + continue; + case "Short": + case "PosShort": + localType = "S"; + break; + case "Byte": + case "PosByte": + localType = "B"; + break; + case "Char": + localType = "C"; + break; + case "Float": + localType = "F"; + break; + case "LongHi": + case "LongLo": + localType = "J"; + break; + case "DoubleHi": + case "DoubleLo": + localType = "D"; + break; + default: + System.err.println(line); + System.err.println(m.group(2)); + System.err.println(m.group(3)); + assert false; + } - private boolean processDirective(String line) { - String line2 = line.substring(1); - if (line2.startsWith("line ") || line2.equals("prologue") - || line2.startsWith("parameter") || line2.startsWith("local ") - || line2.startsWith("end local ")) { - return false; - } + mInitializedRegisters.add(localName); + mOut.append(".local ").append(localName).append(", ").append('"') + .append(localName).append('"').append(':').append(localType) + .append('\n'); + } - append(line); - if (line2.equals("end method")) { - return true; - } - if (line2.startsWith("annotation ") || line2.equals("sparse-switch") - || line2.startsWith("packed-switch ") - || line2.startsWith("array-data ")) { - while (true) { - line2 = nextAndAppend(); - if (line2.startsWith(".end ")) { - break; - } - } - } - return false; - } + return false; + } - private boolean processInstruction(String line) { - if (mFirstInstruction) { - mOut.append(".prologue\n"); - mFirstInstruction = false; - } - mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line) - .append('\n'); + private boolean processDirective(String line) { + String line2 = line.substring(1); + if (line2.startsWith("line ") || line2.startsWith("local ") || line2.startsWith("end local ")) { + return false; + } + if (line2.equals("prologue")) { + if (! areParamsInjected) { + injectRemainingParams(); + } + return false; + } + if (line2.equals("param")) { + mOut.append(".param \"p").append(currParam++).append("\"\n"); + return false; + } + if (line2.startsWith("param")) { + mOut.append(line).append("\n"); + currParam++; + return false; + } - return false; - } + append(line); + if (line2.equals("end method")) { + return true; + } + if (line2.startsWith("annotation ") || line2.equals("sparse-switch") + || line2.startsWith("packed-switch ") + || line2.startsWith("array-data ")) { + while (true) { + line2 = nextAndAppend(); + if (line2.startsWith(".end ")) { + break; + } + } + } + return false; + } - private String next() { - return mIt.next().trim(); - } + private boolean processInstruction(String line) { + if (mFirstInstruction) { + mOut.append(".prologue\n"); + mFirstInstruction = false; + } + mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line) + .append('\n'); - private String nextAndAppend() { - String line = next(); - append(line); - return line; - } + return false; + } - private void append(String append) { - mOut.append(append).append('\n'); - } + private String next() { + return mIt.next().split("//", 2)[1].trim(); + } - private final ListIterator mIt; - private final StringBuilder mOut; + private String nextAndAppend() { + String line = next(); + append(line); + return line; + } - private boolean mFirstInstruction = true; - private final Set mInitializedRegisters = new HashSet(); + private void append(String append) { + mOut.append(append).append('\n'); + } - private static final Pattern REGISTER_INFO_PATTERN = Pattern - .compile("((?:p|v)\\d+)=\\(([^)]+)\\);"); + private final ListIterator mIt; + private final StringBuilder mOut; + + private boolean mFirstInstruction = true; + private final Set mInitializedRegisters = new HashSet(); + + private static final Pattern REGISTER_INFO_PATTERN = Pattern + .compile("((?:p|v)\\d+)=\\(([^,)]+)([^)]*)\\);"); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java deleted file mode 100644 index 6059db64..00000000 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/DexFileBuilder.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2011 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package brut.androlib.src; - -import brut.androlib.AndrolibException; -import brut.androlib.mod.SmaliMod; -import java.io.*; -import org.antlr.runtime.RecognitionException; -import org.jf.dexlib.CodeItem; -import org.jf.dexlib.DexFile; -import org.jf.dexlib.Util.ByteArrayAnnotatedOutput; - -/** - * @author Ryszard Wiśniewski - */ -public class DexFileBuilder { - public void addSmaliFile(File smaliFile) throws AndrolibException { - try { - addSmaliFile(new FileInputStream(smaliFile), - smaliFile.getAbsolutePath()); - } catch (FileNotFoundException ex) { - throw new AndrolibException(ex); - } - } - - public void addSmaliFile(InputStream smaliStream, String name) - throws AndrolibException { - try { - if (!SmaliMod.assembleSmaliFile(smaliStream, name, mDexFile, false, - false, false)) { - throw new AndrolibException("Could not smali file: " + name); - } - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (RecognitionException ex) { - throw new AndrolibException(ex); - } - } - - public void writeTo(File dexFile) throws AndrolibException { - try { - OutputStream out = new FileOutputStream(dexFile); - out.write(getAsByteArray()); - out.close(); - } catch (IOException ex) { - throw new AndrolibException("Could not write dex to file: " - + dexFile, ex); - } - } - - public byte[] getAsByteArray() { - mDexFile.place(); - for (CodeItem codeItem : mDexFile.CodeItemsSection.getItems()) { - codeItem.fixInstructions(true, true); - } - - mDexFile.place(); - - ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); - mDexFile.writeTo(out); - byte[] bytes = out.toByteArray(); - - DexFile.calcSignature(bytes); - DexFile.calcChecksum(bytes); - - return bytes; - } - - private final DexFile mDexFile = new DexFile(); -} diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java index 16992c29..29bac1be 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java @@ -17,6 +17,7 @@ package brut.androlib.src; import brut.androlib.AndrolibException; +import brut.androlib.mod.SmaliMod; import brut.androlib.res.util.ExtFile; import brut.directory.DirectoryException; import java.io.*; @@ -24,92 +25,104 @@ import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.logging.Logger; + +import org.antlr.runtime.RecognitionException; import org.apache.commons.io.IOUtils; +import org.jf.dexlib2.writer.builder.DexBuilder; +import org.jf.dexlib2.writer.io.FileDataStore; /** * @author Ryszard Wiśniewski */ public class SmaliBuilder { - public static void build(ExtFile smaliDir, File dexFile, - HashMap flags) throws AndrolibException { - new SmaliBuilder(smaliDir, dexFile, flags).build(); - } + public static void build(ExtFile smaliDir, File dexFile, + HashMap flags) throws AndrolibException { + new SmaliBuilder(smaliDir, dexFile, flags).build(); + } - private SmaliBuilder(ExtFile smaliDir, File dexFile, - HashMap flags) { - mSmaliDir = smaliDir; - mDexFile = dexFile; - mFlags = flags; - } + private SmaliBuilder(ExtFile smaliDir, File dexFile, + HashMap flags) { + mSmaliDir = smaliDir; + mDexFile = dexFile; + mFlags = flags; + } - private void build() throws AndrolibException { - try { - mDexBuilder = new DexFileBuilder(); - for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { - buildFile(fileName); - } - mDexBuilder.writeTo(mDexFile); - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - } + private void build() throws AndrolibException { + try { + DexBuilder dexBuilder = DexBuilder.makeDexBuilder(); - private void buildFile(String fileName) throws AndrolibException, - IOException { - File inFile = new File(mSmaliDir, fileName); - InputStream inStream = new FileInputStream(inFile); + for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { + buildFile(fileName, dexBuilder); + } + dexBuilder.writeTo(new FileDataStore( new File(mDexFile.getAbsolutePath()))); + } catch (IOException | DirectoryException ex) { + throw new AndrolibException(ex); + } + } - if (fileName.endsWith(".smali")) { - mDexBuilder.addSmaliFile(inFile); - return; - } - if (!fileName.endsWith(".java")) { - LOGGER.warning("Unknown file type, ignoring: " + inFile); - return; - } + private void buildFile(String fileName, DexBuilder dexBuilder) throws AndrolibException, + IOException { + File inFile = new File(mSmaliDir, fileName); + InputStream inStream = new FileInputStream(inFile); - StringBuilder out = new StringBuilder(); - List lines = IOUtils.readLines(inStream); + if (fileName.endsWith(".smali")) { + try { + if (!SmaliMod.assembleSmaliFile(inFile,dexBuilder, false, false)) { + throw new AndrolibException("Could not smali file: " + fileName); + } + } catch (IOException | RecognitionException ex) { + throw new AndrolibException(ex); + } + return; + } + if (!fileName.endsWith(".java")) { + LOGGER.warning("Unknown file type, ignoring: " + inFile); + return; + } - if (!mFlags.containsKey("debug")) { - final String[] linesArray = lines.toArray(new String[0]); - for (int i = 2; i < linesArray.length - 2; i++) { - out.append(linesArray[i]).append('\n'); - } - } else { - lines.remove(lines.size() - 1); - lines.remove(lines.size() - 1); - ListIterator it = lines.listIterator(2); + StringBuilder out = new StringBuilder(); + List lines = IOUtils.readLines(inStream); - out.append(".source \"").append(inFile.getName()).append("\"\n"); - while (it.hasNext()) { - String line = it.next().trim(); - if (line.isEmpty() || line.charAt(0) == '#' - || line.startsWith(".source")) { - continue; - } - if (line.startsWith(".method ")) { - it.previous(); - DebugInjector.inject(it, out); - continue; - } + if (!mFlags.get("debug")) { + final String[] linesArray = lines.toArray(new String[0]); + for (int i = 1; i < linesArray.length - 1; i++) { + out.append(linesArray[i].split("//", 2)[1]).append('\n'); + } + } else { + lines.remove(lines.size() - 1); + ListIterator it = lines.listIterator(1); - out.append(line).append('\n'); - } - } - mDexBuilder.addSmaliFile(IOUtils.toInputStream(out.toString()), - fileName); - } + out.append(".source \"").append(inFile.getName()).append("\"\n"); + while (it.hasNext()) { + String line = it.next().split("//", 2)[1].trim(); + if (line.isEmpty() || line.charAt(0) == '#' + || line.startsWith(".source")) { + continue; + } + if (line.startsWith(".method ")) { + it.previous(); + DebugInjector.inject(it, out); + continue; + } - private final ExtFile mSmaliDir; - private final File mDexFile; - private final HashMap mFlags; + out.append(line).append('\n'); + } + } - private DexFileBuilder mDexBuilder; + try { + if (!SmaliMod.assembleSmaliFile(out.toString(),dexBuilder, false, false, inFile)) { + throw new AndrolibException("Could not smali file: " + fileName); + } + } catch (IOException | RecognitionException ex) { + throw new AndrolibException(ex); + } + } - private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class - .getName()); + private final ExtFile mSmaliDir; + private final File mDexFile; + private final HashMap mFlags; + + private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class + .getName()); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java index 248e0dc3..af43f723 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java @@ -17,42 +17,138 @@ package brut.androlib.src; import brut.androlib.AndrolibException; +import org.jf.baksmali.baksmali; +import org.jf.baksmali.baksmaliOptions; +import org.jf.dexlib2.DexFileFactory; +import org.jf.dexlib2.analysis.ClassPath; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; +import org.jf.dexlib2.dexbacked.DexBackedOdexFile; +import org.jf.dexlib2.analysis.InlineMethodResolver; + +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; import java.io.IOException; -import org.jf.baksmali.baksmali; -import org.jf.dexlib.DexFile; +import java.nio.charset.Charset; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; /** * @author Ryszard Wiśniewski */ public class SmaliDecoder { - public static void decode(File apkFile, File outDir, boolean debug, - boolean bakdeb) throws AndrolibException { - new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode(); - } + public static void decode(File apkFile, File outDir, boolean debug, String debugLinePrefix, + boolean bakdeb, int api) throws AndrolibException { + new SmaliDecoder(apkFile, outDir, debug, debugLinePrefix, bakdeb, api).decode(); + } - private SmaliDecoder(File apkFile, File outDir, boolean debug, - boolean bakdeb) { - mApkFile = apkFile; - mOutDir = outDir; - mDebug = debug; - mBakDeb = bakdeb; - } + private SmaliDecoder(File apkFile, File outDir, boolean debug, String debugLinePrefix, + boolean bakdeb, int api) { + mApkFile = apkFile; + mOutDir = outDir.toPath(); + mDebug = debug; + mDebugLinePrefix = debugLinePrefix; + mBakDeb = bakdeb; + mApi = api; + } - private void decode() throws AndrolibException { - try { - baksmali.disassembleDexFile(mApkFile.getAbsolutePath(), - new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), - null, null, null, false, true, true, mBakDeb, false, false, - 0, false, false, null, false); - } catch (IOException ex) { - throw new AndrolibException(ex); - } - } + private void decode() throws AndrolibException { + try { + ClassPath.dontLoadClassPath = mDebug; - private final File mApkFile; - private final File mOutDir; - private final boolean mDebug; - private final boolean mBakDeb; + baksmaliOptions options = new baksmaliOptions(); + + // options + options.deodex = false; + options.outputDirectory = mOutDir.toAbsolutePath().toString(); + options.noParameterRegisters = false; + options.useLocalsDirective = true; + options.useSequentialLabels = true; + options.outputDebugInfo = mBakDeb; + options.addCodeOffsets = false; + options.jobs = -1; + options.noAccessorComments = false; + options.registerInfo = (mDebug ? baksmaliOptions.DIFFPRE : 0); + options.ignoreErrors = false; + options.inlineResolver = null; + options.checkPackagePrivateAccess = false; + + // set jobs automatically + if (options.jobs <= 0) { + if (mDebug) { + options.jobs = 1; + } else { + options.jobs = Runtime.getRuntime().availableProcessors(); + if (options.jobs > 6) { + options.jobs = 6; + } + } + } + + // create the dex + DexBackedDexFile dexFile = DexFileFactory.loadDexFile(mApkFile, mApi); + + if (dexFile.isOdexFile()) { + throw new AndrolibException("Warning: You are disassembling an odex file without deodexing it."); + } + + if (dexFile instanceof DexBackedOdexFile) { + options.inlineResolver = + InlineMethodResolver.createInlineMethodResolver(((DexBackedOdexFile)dexFile).getOdexVersion()); + } + + baksmali.disassembleDexFile(dexFile,options); + + if (mDebug) { + Files.walkFileTree(mOutDir, new SmaliFileVisitor()); + } + } catch (IOException ex) { + throw new AndrolibException(ex); + } + } + + private final File mApkFile; + private final Path mOutDir; + private final boolean mDebug; + private final String mDebugLinePrefix; + private final boolean mBakDeb; + private final int mApi; + + + private class SmaliFileVisitor extends SimpleFileVisitor { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String fileName = file.getFileName().toString(); + if (! fileName.endsWith(".smali")) { + return FileVisitResult.CONTINUE; + } + fileName = fileName.substring(0, fileName.length() - 6); + try ( + BufferedReader in = Files.newBufferedReader(file, Charset.defaultCharset()); + BufferedWriter out = Files.newBufferedWriter( + file.resolveSibling(fileName + ".java"), Charset.defaultCharset()) + ) { + TypeName type = TypeName.fromPath(mOutDir.relativize(file.resolveSibling(fileName))); + out.write("package " + type.package_ + "; class " + type.getName(true, true) + " { void a() { int a;"); + out.newLine(); + + String line; + final String debugLinePrefix = mDebugLinePrefix; + while ((line = in.readLine()) != null) { + out.write(debugLinePrefix); + out.write(line); + out.newLine(); + } + + out.write("}}"); + out.newLine(); + } + Files.delete(file); + return FileVisitResult.CONTINUE; + } + } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/TypeName.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/TypeName.java index f790d03a..29480fa3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/TypeName.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/TypeName.java @@ -18,190 +18,195 @@ package brut.androlib.src; import brut.androlib.AndrolibException; import brut.util.Duo; +import com.google.common.base.Joiner; + import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** * @author Ryszard Wiśniewski */ public class TypeName { - public final String package_; - public final String type; - public final String innerType; - public final int array; + public final String package_; + public final String type; + public final String innerType; + public final int array; - public TypeName(String type, int array) { - this(null, type, null, array); - } + public TypeName(String type, int array) { + this(null, type, null, array); + } - public TypeName(String package_, String type, String innerType, int array) { - this.package_ = package_; - this.type = type; - this.innerType = innerType; - this.array = array; - } + public TypeName(String package_, String type, String innerType, int array) { + this.package_ = package_; + this.type = type; + this.innerType = innerType; + this.array = array; + } - public String getShortenedName() { - return getName("java.lang".equals(package_), isFileOwner()); - } + public String getShortenedName() { + return getName("java.lang".equals(package_), isFileOwner()); + } - public String getName() { - return getName(false, false); - } + public String getName() { + return getName(false, false); + } - public String getName(boolean excludePackage, boolean separateInner) { - String name = (package_ == null || excludePackage ? "" : package_ + '.') - + type - + (innerType != null ? (separateInner ? '$' : '.') + innerType - : ""); - for (int i = 0; i < array; i++) { - name += "[]"; - } - return name; - } + public String getName(boolean excludePackage, boolean separateInner) { + String name = (package_ == null || excludePackage ? "" : package_ + '.') + + type + + (innerType != null ? (separateInner ? '$' : '.') + innerType + : ""); + for (int i = 0; i < array; i++) { + name += "[]"; + } + return name; + } - public String getJavaFilePath() { - return getFilePath(isFileOwner()) + ".java"; - } + public String getJavaFilePath() { + return getFilePath(isFileOwner()) + ".java"; + } - public String getSmaliFilePath() { - return getFilePath(true) + ".smali"; - } + public String getSmaliFilePath() { + return getFilePath(true) + ".smali"; + } - public String getFilePath(boolean separateInner) { - return package_.replace('.', File.separatorChar) + File.separatorChar - + type + (separateInner && isInner() ? "$" + innerType : ""); - } + public String getFilePath(boolean separateInner) { + return package_.replace('.', File.separatorChar) + File.separatorChar + + type + (separateInner && isInner() ? "$" + innerType : ""); + } - public boolean isInner() { - return innerType != null; - } + public boolean isInner() { + return innerType != null; + } - public boolean isArray() { - return array != 0; - } + public boolean isArray() { + return array != 0; + } - public boolean isFileOwner() { - if (mIsFileOwner == null) { - mIsFileOwner = true; - if (isInner()) { - char c = innerType.charAt(0); - if (c < '0' || c > '9') { - mIsFileOwner = false; - } - } - } - return mIsFileOwner; - } + public boolean isFileOwner() { + if (mIsFileOwner == null) { + mIsFileOwner = true; + if (isInner()) { + char c = innerType.charAt(0); + if (c < '0' || c > '9') { + mIsFileOwner = false; + } + } + } + return mIsFileOwner; + } - @Override - public String toString() { - return getName(); - } + @Override + public String toString() { + return getName(); + } - public static TypeName fromInternalName(String internal) - throws AndrolibException { - Duo duo = fetchFromInternalName(internal); - if (duo.m2 != internal.length()) { - throw new AndrolibException("Invalid internal name: " + internal); - } - return duo.m1; - } + public static TypeName fromInternalName(String internal) + throws AndrolibException { + Duo duo = fetchFromInternalName(internal); + if (duo.m2 != internal.length()) { + throw new AndrolibException("Invalid internal name: " + internal); + } + return duo.m1; + } - public static List listFromInternalName(String internal) - throws AndrolibException { - List types = new ArrayList(); - while (!internal.isEmpty()) { - Duo duo = fetchFromInternalName(internal); - types.add(duo.m1); - internal = internal.substring(duo.m2); - } - return types; - } + public static List listFromInternalName(String internal) + throws AndrolibException { + List types = new ArrayList(); + while (!internal.isEmpty()) { + Duo duo = fetchFromInternalName(internal); + types.add(duo.m1); + internal = internal.substring(duo.m2); + } + return types; + } - public static Duo fetchFromInternalName(String internal) - throws AndrolibException { - String origInternal = internal; - int array = 0; + public static TypeName fromPath(Path path) { + List parts = new ArrayList<>(path.getNameCount()); + for (Path p : path) { + parts.add(p.toString()); + } + return fromNameParts(parts, 0); + } - boolean isArray = false; - do { - if (internal.isEmpty()) { - throw new AndrolibException("Invalid internal name: " - + origInternal); - } - isArray = internal.charAt(0) == '['; - if (isArray) { - array++; - internal = internal.substring(1); - } - } while (isArray); + public static TypeName fromNameParts(List parts, int array) { + String type = parts.get(parts.size() - 1); + parts = parts.subList(0, parts.size() - 1); + String innerType = null; - int length = array + 1; - String package_ = null; - String type = null; - String innerType = null; - switch (internal.charAt(0)) { - case 'B': - type = "byte"; - break; - case 'C': - type = "char"; - break; - case 'D': - type = "double"; - break; - case 'F': - type = "float"; - break; - case 'I': - type = "int"; - break; - case 'J': - type = "long"; - break; - case 'S': - type = "short"; - break; - case 'Z': - type = "boolean"; - break; - case 'V': - type = "void"; - break; - case 'L': - int pos = internal.indexOf(';'); - if (pos == -1) { - throw new AndrolibException("Invalid internal name: " - + origInternal); - } - length += pos; - internal = internal.substring(1, pos); + int pos = type.indexOf('$'); + if (pos != -1) { + innerType = type.substring(pos + 1); + type = type.substring(0, pos); + } + return new TypeName(Joiner.on('.').join(parts), type, innerType, array); + } - pos = internal.lastIndexOf('/'); - if (pos == -1) { - package_ = ""; - type = internal; - } else { - package_ = internal.substring(0, pos).replace('/', '.'); - type = internal.substring(pos + 1); - } + public static Duo fetchFromInternalName(String internal) + throws AndrolibException { + String origInternal = internal; + int array = 0; - pos = type.indexOf('$'); - if (pos != -1) { - innerType = type.substring(pos + 1); - type = type.substring(0, pos); - } - break; - default: - throw new AndrolibException("Invalid internal name: " - + origInternal); - } + boolean isArray = false; + do { + if (internal.isEmpty()) { + throw new AndrolibException("Invalid internal name: " + + origInternal); + } + isArray = internal.charAt(0) == '['; + if (isArray) { + array++; + internal = internal.substring(1); + } + } while (isArray); - return new Duo(new TypeName(package_, type, - innerType, array), length); - } + int length = array + 1; + String type; + switch (internal.charAt(0)) { + case 'B': + type = "byte"; + break; + case 'C': + type = "char"; + break; + case 'D': + type = "double"; + break; + case 'F': + type = "float"; + break; + case 'I': + type = "int"; + break; + case 'J': + type = "long"; + break; + case 'S': + type = "short"; + break; + case 'Z': + type = "boolean"; + break; + case 'V': + type = "void"; + break; + case 'L': + int pos = internal.indexOf(';'); + if (pos == -1) { + throw new AndrolibException("Invalid internal name: " + + origInternal); + } + return new Duo<>(fromNameParts(Arrays.asList(internal.substring(1, pos).split("/")), array), length + pos); + default: + throw new AndrolibException("Invalid internal name: " + + origInternal); + } - private Boolean mIsFileOwner; + return new Duo<>(new TypeName(null, type, null, array), length); + } + + private Boolean mIsFileOwner; } diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/aapt b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/aapt new file mode 100755 index 00000000..b0a97f94 Binary files /dev/null and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/linux/aapt differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/aapt b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/aapt new file mode 100644 index 00000000..039edf4e Binary files /dev/null and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/macosx/aapt differ diff --git a/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/windows/aapt.exe b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/windows/aapt.exe new file mode 100755 index 00000000..eff34c5b Binary files /dev/null and b/brut.apktool/apktool-lib/src/main/resources/prebuilt/aapt/windows/aapt.exe differ diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/AaptTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/AaptTest.java new file mode 100644 index 00000000..5f2d30ca --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/AaptTest.java @@ -0,0 +1,49 @@ +/** + * Copyright 2011 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib; + +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +import static org.junit.Assert.assertEquals; + +/** + * @author Connor Tumbleson + */ +public class AaptTest { + + @Test + public void isAaptInstalledTest() throws Exception { + assertEquals(true, isAaptPresent()); + } + + private static boolean isAaptPresent() throws Exception { + boolean result = true; + try { + Process proc = Runtime.getRuntime().exec("aapt"); + BufferedReader br = new BufferedReader(new InputStreamReader( + proc.getErrorStream())); + String line = null; + while ((line = br.readLine()) != null) { + } + } catch (Exception ex) { + result = false; + } + return result; + } +} diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeJarTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeJarTest.java new file mode 100644 index 00000000..63dafb2d --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeJarTest.java @@ -0,0 +1,68 @@ +/** + * Copyright 2011 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib; + +import brut.androlib.res.util.ExtFile; +import brut.common.BrutException; +import brut.util.OS; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.util.logging.Logger; + +import static org.junit.Assert.assertTrue; + +/** + * @author Connor Tumbleson + */ +public class BuildAndDecodeJarTest { + + @BeforeClass + public static void beforeClass() throws Exception, BrutException { + sTmpDir = new ExtFile(OS.createTempDirectory()); + sTestOrigDir = new ExtFile(sTmpDir, "testjar-orig"); + sTestNewDir = new ExtFile(sTmpDir, "testjar-new"); + LOGGER.info("Unpacking testjar..."); + TestUtils.copyResourceDir(BuildAndDecodeJarTest.class, "brut/apktool/testjar/", sTestOrigDir); + + LOGGER.info("Building testjar.apk..."); + File testJar = new File(sTmpDir, "testjar.jar"); + new Androlib().build(sTestOrigDir, testJar, TestUtils.returnStockHashMap(),""); + + LOGGER.info("Decoding testjar.jar..."); + ApkDecoder apkDecoder = new ApkDecoder(testJar); + apkDecoder.setOutDir(sTestNewDir); + apkDecoder.decode(); + } + + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } + + @Test + public void buildAndDecodeTest() throws BrutException { + assertTrue(sTestNewDir.isDirectory()); + } + + private static ExtFile sTmpDir; + private static ExtFile sTestOrigDir; + private static ExtFile sTestNewDir; + + private final static Logger LOGGER = Logger.getLogger(BuildAndDecodeJarTest.class.getName()); +} diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java index 19831387..4a2f65d4 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BuildAndDecodeTest.java @@ -17,10 +17,13 @@ package brut.androlib; import brut.androlib.res.util.ExtFile; import brut.common.BrutException; +import brut.directory.FileDirectory; import brut.util.OS; import java.io.*; -import java.util.HashMap; +import java.util.Map; +import java.util.Set; import java.util.logging.Logger; + import org.custommonkey.xmlunit.*; import org.junit.*; import static org.junit.Assert.*; @@ -31,168 +34,270 @@ import org.xml.sax.SAXException; */ public class BuildAndDecodeTest { - @BeforeClass - public static void beforeClass() throws Exception, BrutException { - sTmpDir = new ExtFile(OS.createTempDirectory()); - sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig"); - sTestNewDir = new ExtFile(sTmpDir, "testapp-new"); - LOGGER.info("Unpacking testapp..."); - TestUtils.copyResourceDir(BuildAndDecodeTest.class, - "brut/apktool/testapp/", sTestOrigDir); - } + @BeforeClass + public static void beforeClass() throws Exception, BrutException { + sTmpDir = new ExtFile(OS.createTempDirectory()); + sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig"); + sTestNewDir = new ExtFile(sTmpDir, "testapp-new"); + LOGGER.info("Unpacking testapp..."); + TestUtils.copyResourceDir(BuildAndDecodeTest.class, + "brut/apktool/testapp/", sTestOrigDir); - @AfterClass - public static void afterClass() throws BrutException { - OS.rmdir(sTmpDir); - } + LOGGER.info("Building testapp.apk..."); + File testApk = new File(sTmpDir, "testapp.apk"); + new Androlib().build(sTestOrigDir, testApk, + TestUtils.returnStockHashMap(),""); - @Test - public void isAaptInstalledTest() throws Exception { - assertEquals(true, isAaptPresent()); - } + LOGGER.info("Decoding testapp.apk..."); + ApkDecoder apkDecoder = new ApkDecoder(testApk); + apkDecoder.setOutDir(sTestNewDir); + apkDecoder.decode(); + } - @Test - public void encodeAndDecodeTest() throws BrutException, IOException { + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } - LOGGER.info("Building testapp.apk..."); - File testApk = new File(sTmpDir, "testapp.apk"); - ExtFile blank = null; - new Androlib().build(sTestOrigDir, testApk, - BuildAndDecodeTest.returnStock(), blank, ""); + @Test + public void buildAndDecodeTest() throws BrutException { + assertTrue(sTestNewDir.isDirectory()); + } - LOGGER.info("Decoding testapp.apk..."); - ApkDecoder apkDecoder = new ApkDecoder(testApk); - apkDecoder.setOutDir(sTestNewDir); - apkDecoder.decode(); - } + @Test + public void valuesAnimsTest() throws BrutException { + compareValuesFiles("values-mcc001/anims.xml"); + } - @Test - public void valuesArraysTest() throws BrutException { - compareValuesFiles("values-mcc001/arrays.xml"); - compareValuesFiles("values-mcc002/arrays.xml"); - } + @Test + public void valuesArraysTest() throws BrutException { + compareValuesFiles("values-mcc001/arrays.xml"); + } - @Test - public void valuesBoolsTest() throws BrutException { - compareValuesFiles("values-mcc001/bools.xml"); - } + @Test + public void valuesArraysCastingTest() throws BrutException { + compareValuesFiles("values-mcc002/arrays.xml"); + compareValuesFiles("values-mcc003/arrays.xml"); + } - @Test - public void valuesColorsTest() throws BrutException { - compareValuesFiles("values-mcc001/colors.xml"); - } + @Test + public void valuesAttrsTest() throws BrutException { + compareValuesFiles("values/attrs.xml"); + } - @Test - public void valuesDimensTest() throws BrutException { - compareValuesFiles("values-mcc001/dimens.xml"); - } + @Test + public void valuesBoolsTest() throws BrutException { + compareValuesFiles("values-mcc001/bools.xml"); + } - @Test - public void valuesIdsTest() throws BrutException { - compareValuesFiles("values-mcc001/ids.xml"); - } + @Test + public void valuesColorsTest() throws BrutException { + compareValuesFiles("values-mcc001/colors.xml"); + } - @Test - public void valuesIntegersTest() throws BrutException { - compareValuesFiles("values-mcc001/integers.xml"); - } + @Test + public void valuesDimensTest() throws BrutException { + compareValuesFiles("values-mcc001/dimens.xml"); + } - @Test - public void valuesStringsTest() throws BrutException { - compareValuesFiles("values-mcc001/strings.xml"); - } + @Test + public void valuesDrawablesTest() throws BrutException { + compareValuesFiles("values-mcc001/drawables.xml"); + } - @Test - public void valuesReferencesTest() throws BrutException { - compareValuesFiles("values-mcc002/strings.xml"); - } + @Test + public void valuesIdsTest() throws BrutException { + compareValuesFiles("values-mcc001/ids.xml"); + } - @Test - public void crossTypeTest() throws BrutException { - compareValuesFiles("values-mcc003/strings.xml"); - compareValuesFiles("values-mcc003/integers.xml"); - compareValuesFiles("values-mcc003/bools.xml"); - } + @Test + public void valuesIntegersTest() throws BrutException { + compareValuesFiles("values-mcc001/integers.xml"); + } - @Test - public void xmlLiteralsTest() throws BrutException { - compareXmlFiles("res/xml/literals.xml"); - } + @Test + public void valuesLayoutsTest() throws BrutException { + compareValuesFiles("values-mcc001/layouts.xml"); + } - @Test - public void xmlReferencesTest() throws BrutException { - compareXmlFiles("res/xml/references.xml"); - } + @Test + public void xmlPluralsTest() throws BrutException { + compareValuesFiles("values-mcc001/plurals.xml"); + } - @Test - public void qualifiersTest() throws BrutException { - compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp" - + "-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key" - + "-navhidden-dpad/strings.xml"); - } + @Test + public void valuesStringsTest() throws BrutException { + compareValuesFiles("values-mcc001/strings.xml"); + } - private static boolean isAaptPresent() throws Exception { - boolean result = true; - try { - Process proc = Runtime.getRuntime().exec("aapt"); - BufferedReader br = new BufferedReader(new InputStreamReader( - proc.getErrorStream())); - String line = null; - while ((line = br.readLine()) != null) { - } - } catch (Exception ex) { - result = false; - } - return result; - } + @Test + public void valuesStylesTest() throws BrutException { + compareValuesFiles("values-mcc001/styles.xml"); + } - private void compareValuesFiles(String path) throws BrutException { - compareXmlFiles("res/" + path, new ElementNameAndAttributeQualifier( - "name")); - } + @Test + public void valuesReferencesTest() throws BrutException { + compareValuesFiles("values-mcc002/strings.xml"); + } - private void compareXmlFiles(String path) throws BrutException { - compareXmlFiles(path, null); - } + @Test + public void valuesExtraLongTest() throws BrutException { + compareValuesFiles("values-en/strings.xml"); + } - private void compareXmlFiles(String path, ElementQualifier qualifier) - throws BrutException { - DetailedDiff diff; - try { - Reader control = new FileReader(new File(sTestOrigDir, path)); - Reader test = new FileReader(new File(sTestNewDir, path)); + @Test + public void crossTypeTest() throws BrutException { + compareValuesFiles("values-mcc003/strings.xml"); + compareValuesFiles("values-mcc003/integers.xml"); + compareValuesFiles("values-mcc003/bools.xml"); + } - diff = new DetailedDiff(new Diff(control, test)); - } catch (SAXException ex) { - throw new BrutException(ex); - } catch (IOException ex) { - throw new BrutException(ex); - } + @Test + public void xmlLiteralsTest() throws BrutException { + compareXmlFiles("res/xml/literals.xml"); + } - if (qualifier != null) { - diff.overrideElementQualifier(qualifier); - } + @Test + public void xmlReferencesTest() throws BrutException { + compareXmlFiles("res/xml/references.xml"); + } - assertTrue(path + ": " + diff.getAllDifferences().toString(), - diff.similar()); - } + @Test + public void qualifiersTest() throws BrutException { + compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp" + + "-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key" + + "-navhidden-dpad/strings.xml"); + } - private static HashMap returnStock() throws BrutException { - HashMap tmp = new HashMap(); - tmp.put("forceBuildAll", false); - tmp.put("debug", false); - tmp.put("verbose", false); - tmp.put("injectOriginal", false); - tmp.put("framework", false); - tmp.put("update", false); + @Test + public void drawableNoDpiTest() throws BrutException, IOException { + compareResFolder("drawable-nodpi"); + } - return tmp; - } + @Test + public void drawableNumberedDpiTest() throws BrutException, IOException { + compareResFolder("drawable-534dpi"); + } - private static ExtFile sTmpDir; - private static ExtFile sTestOrigDir; - private static ExtFile sTestNewDir; + @Test + public void drawableLdpiTest() throws BrutException, IOException { + compareResFolder("drawable-ldpi"); + } - private final static Logger LOGGER = Logger - .getLogger(BuildAndDecodeTest.class.getName()); + @Test + public void drawableMdpiTest() throws BrutException, IOException { + compareResFolder("drawable-mdpi"); + } + + @Test + public void drawableTvdpiTest() throws BrutException, IOException { + compareResFolder("drawable-tvdpi"); + } + + @Test + public void drawableXhdpiTest() throws BrutException, IOException { + compareResFolder("drawable-xhdpi"); + } + + @Test + public void drawableXxhdpiTest() throws BrutException, IOException { + compareResFolder("drawable-xxhdpi"); + } + + @Test + public void resRawTest() throws BrutException, IOException { + compareResFolder("raw"); + } + + @Test + public void libsTest() throws BrutException, IOException { + compareLibsFolder("libs"); + } + + @Test + public void unknownFolderTest() throws BrutException, IOException { + compareUnknownFiles(); + } + + private void compareUnknownFiles() throws BrutException, IOException { + Map control = new Androlib().readMetaFile(sTestOrigDir); + Map test = new Androlib().readMetaFile(sTestNewDir); + assertTrue(control.containsKey("unknownFiles")); + assertTrue(test.containsKey("unknownFiles")); + + Map control_files = (Map)control.get("unknownFiles"); + Map test_files = (Map)test.get("unknownFiles"); + assertTrue(control_files.size() == test_files.size()); + } + + private boolean compareBinaryFolder(String path, boolean res) throws BrutException, IOException { + + String tmp = ""; + if (res) { + tmp = File.separatorChar + "res" + File.separatorChar; + } + + FileDirectory fileDirectory = new FileDirectory(sTestOrigDir + tmp + path); + + Set files = fileDirectory.getFiles(true); + for (String filename : files) { + File control = new File(filename); + + // hacky fix - load test by changing name of control + File test = new File(control.toString().replace("testapp-orig", "testapp-new")); + + if (test.isFile()) { + if (control.hashCode() != test.hashCode()) { + return false; + } + } + } + return true; + } + + private boolean compareResFolder(String path) throws BrutException, IOException { + return compareBinaryFolder(path, true); + } + + private boolean compareLibsFolder(String path) throws BrutException, IOException { + return compareBinaryFolder(File.separatorChar + path,false); + } + + private void compareValuesFiles(String path) throws BrutException { + compareXmlFiles("res/" + path, new ElementNameAndAttributeQualifier( + "name")); + } + + private void compareXmlFiles(String path) throws BrutException { + compareXmlFiles(path, null); + } + + private void compareXmlFiles(String path, ElementQualifier qualifier) + throws BrutException { + DetailedDiff diff; + try { + Reader control = new FileReader(new File(sTestOrigDir, path)); + Reader test = new FileReader(new File(sTestNewDir, path)); + + diff = new DetailedDiff(new Diff(control, test)); + } catch (SAXException ex) { + throw new BrutException(ex); + } catch (IOException ex) { + throw new BrutException(ex); + } + + if (qualifier != null) { + diff.overrideElementQualifier(qualifier); + } + + assertTrue(path + ": " + diff.getAllDifferences().toString(), + diff.similar()); + } + + private static ExtFile sTmpDir; + private static ExtFile sTestOrigDir; + private static ExtFile sTestNewDir; + + private final static Logger LOGGER = Logger + .getLogger(BuildAndDecodeTest.class.getName()); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java index f422c165..a66c7d9a 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java @@ -32,110 +32,122 @@ import org.xmlpull.v1.*; */ public abstract class TestUtils { - public static Map parseStringsXml(File file) - throws BrutException { - try { - XmlPullParser xpp = XmlPullParserFactory.newInstance() - .newPullParser(); - xpp.setInput(new FileReader(file)); + public static Map parseStringsXml(File file) + throws BrutException { + try { + XmlPullParser xpp = XmlPullParserFactory.newInstance() + .newPullParser(); + xpp.setInput(new FileReader(file)); - int eventType; - String key = null; - Map map = new HashMap(); - while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) { - switch (eventType) { - case XmlPullParser.START_TAG: - if ("string".equals(xpp.getName())) { - int attrCount = xpp.getAttributeCount(); - for (int i = 0; i < attrCount; i++) { - if ("name".equals(xpp.getAttributeName(i))) { - key = xpp.getAttributeValue(i); - break; - } - } - } - break; - case XmlPullParser.END_TAG: - if ("string".equals(xpp.getName())) { - key = null; - } - break; - case XmlPullParser.TEXT: - if (key != null) { - map.put(key, xpp.getText()); - } - break; - } - } + int eventType; + String key = null; + Map map = new HashMap(); + while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) { + switch (eventType) { + case XmlPullParser.START_TAG: + if ("string".equals(xpp.getName())) { + int attrCount = xpp.getAttributeCount(); + for (int i = 0; i < attrCount; i++) { + if ("name".equals(xpp.getAttributeName(i))) { + key = xpp.getAttributeValue(i); + break; + } + } + } + break; + case XmlPullParser.END_TAG: + if ("string".equals(xpp.getName())) { + key = null; + } + break; + case XmlPullParser.TEXT: + if (key != null) { + map.put(key, xpp.getText()); + } + break; + } + } - return map; - } catch (IOException ex) { - throw new BrutException(ex); - } catch (XmlPullParserException ex) { - throw new BrutException(ex); - } - } + return map; + } catch (IOException ex) { + throw new BrutException(ex); + } catch (XmlPullParserException ex) { + throw new BrutException(ex); + } + } - /* - * TODO: move to brut.util.Jar - it's not possible for now, because below - * implementation uses brut.dir. I think I should merge all my projects to - * single brut.common . - */ - public static void copyResourceDir(Class class_, String dirPath, File out) - throws BrutException { - if (!out.exists()) { - out.mkdirs(); - } - copyResourceDir(class_, dirPath, new FileDirectory(out)); - } + public static HashMap returnStockHashMap() throws BrutException { + HashMap tmp = new HashMap(); + tmp.put("forceBuildAll", false); + tmp.put("debug", false); + tmp.put("verbose", false); + tmp.put("framework", false); + tmp.put("update", false); + tmp.put("copyOriginal", false); - public static void copyResourceDir(Class class_, String dirPath, - Directory out) throws BrutException { - if (class_ == null) { - class_ = Class.class; - } + return tmp; + } - URL dirURL = class_.getClassLoader().getResource(dirPath); - if (dirURL != null && dirURL.getProtocol().equals("file")) { - DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out); - return; - } + /* + * TODO: move to brut.util.Jar - it's not possible for now, because below + * implementation uses brut.dir. I think I should merge all my projects to + * single brut.common . + */ + public static void copyResourceDir(Class class_, String dirPath, File out) + throws BrutException { + if (!out.exists()) { + out.mkdirs(); + } + copyResourceDir(class_, dirPath, new FileDirectory(out)); + } - if (dirURL == null) { - String className = class_.getName().replace(".", "/") + ".class"; - dirURL = class_.getClassLoader().getResource(className); - } + public static void copyResourceDir(Class class_, String dirPath, + Directory out) throws BrutException { + if (class_ == null) { + class_ = Class.class; + } - if (dirURL.getProtocol().equals("jar")) { - String jarPath; - try { - jarPath = URLDecoder.decode( - dirURL.getPath().substring(5, - dirURL.getPath().indexOf("!")), "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new BrutException(ex); - } - DirUtil.copyToDir(new FileDirectory(jarPath), out); - } - } + URL dirURL = class_.getClassLoader().getResource(dirPath); + if (dirURL != null && dirURL.getProtocol().equals("file")) { + DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out); + return; + } - public static class ResValueElementQualifier implements ElementQualifier { + if (dirURL == null) { + String className = class_.getName().replace(".", "/") + ".class"; + dirURL = class_.getClassLoader().getResource(className); + } - @Override - public boolean qualifyForComparison(Element control, Element test) { - String controlType = control.getTagName(); - if ("item".equals(controlType)) { - controlType = control.getAttribute("type"); - } + if (dirURL.getProtocol().equals("jar")) { + String jarPath; + try { + jarPath = URLDecoder.decode( + dirURL.getPath().substring(5, + dirURL.getPath().indexOf("!")), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new BrutException(ex); + } + DirUtil.copyToDir(new FileDirectory(jarPath), out); + } + } - String testType = test.getTagName(); - if ("item".equals(testType)) { - testType = test.getAttribute("type"); - } + public static class ResValueElementQualifier implements ElementQualifier { - return controlType.equals(testType) - && control.getAttribute("name").equals( - test.getAttribute("name")); - } - } + @Override + public boolean qualifyForComparison(Element control, Element test) { + String controlType = control.getTagName(); + if ("item".equals(controlType)) { + controlType = control.getAttribute("type"); + } + + String testType = test.getTagName(); + if ("item".equals(testType)) { + testType = test.getAttribute("type"); + } + + return controlType.equals(testType) + && control.getAttribute("name").equals( + test.getAttribute("name")); + } + } } diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/AndroidManifest.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/AndroidManifest.xml index d79f6186..6863df42 100644 --- a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/AndroidManifest.xml +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/AndroidManifest.xml @@ -1,3 +1,2 @@ - + diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/apktool.yml index dbe200bc..51536bc9 100644 --- a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/apktool.yml @@ -1,6 +1,16 @@ -version: 1.5.0 +version: 2.0.0 apkFileName: testapp.apk isFrameworkApk: false usesFramework: ids: - 1 +packageInfo: + forced-package-id: '127' +versionInfo: + versionCode: '1' + versionName: '1.0' +compressionType: false +unknownFiles: + hidden.file: '8' + unk_folder/unknown_file: '8' + lib_bug603/bug603: '8' \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/libs/android-support-v13.jar b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/libs/android-support-v13.jar new file mode 100644 index 00000000..57b70721 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/libs/android-support-v13.jar differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-534dpi/ic_launcher.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-534dpi/ic_launcher.png new file mode 100644 index 00000000..96a442e5 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-534dpi/ic_launcher.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-hdpi/ic_launcher.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..96a442e5 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-hdpi/ic_launcher.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-ldpi/data.jpg b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-ldpi/data.jpg new file mode 100644 index 00000000..81a87b17 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-ldpi/data.jpg differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-ldpi/ic_launcher.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 00000000..99238729 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-ldpi/ic_launcher.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-mdpi/ic_launcher.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..359047df Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-mdpi/ic_launcher.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-nodpi/ic_launcher.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-nodpi/ic_launcher.png new file mode 100644 index 00000000..359047df Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-nodpi/ic_launcher.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-tvdpi/ic_launcher.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-tvdpi/ic_launcher.png new file mode 100644 index 00000000..359047df Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-tvdpi/ic_launcher.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/9patch.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/9patch.9.png new file mode 100644 index 00000000..286ab358 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/9patch.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/9patch_htc.r.9.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/9patch_htc.r.9.png new file mode 100644 index 00000000..286ab358 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/9patch_htc.r.9.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/ic_launcher.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000..71c6d760 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xhdpi/ic_launcher.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/ic_launcher.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..359047df Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/raw/blank_test.png b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/raw/blank_test.png new file mode 100644 index 00000000..a1d202bc Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/raw/blank_test.png differ diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-en/strings.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-en/strings.xml new file mode 100644 index 00000000..da48b040 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-en/strings.xml @@ -0,0 +1,8 @@ + + + g7sVCIOAIBKwWDzQKE0ThAJKDCPzA5kurfUIPvIaDjX97kuv2lDFS75wF6GVYEMOSrMH0H1MfVMfMgpOXH4LBNuzeGTe8HstiRTzbOnw3wCT7HYsOvfUB11Lq2myfr9vzHCLAdx30m4DPJ2t0QRIk20I1GeF3PzynpQsY550uyYLuu4T7Xwna0eRS26wk02lIDM6eyVKkm4ZpXc6VkLL2p0ibXVjVkalf1f2x5OND5YdQlOOCaz1jMzXZS2xtOqegy74hJrl0UpNiRcyPs8P9wpf6KQS1yHUnUswGmTBsGEvEA39vFkWTgsOKKrVcYMzA4KhZYoeEotgUbVpiAUrcKfJDZtfCAzIVW2RXzPWwtiSYOXDO6oBfDphNQa4B0OwC6m9KmvTwWv2ezjUuwfdJHv2o8vr830PovzkNzwGG3CesyrQWLHalwgMq01UXJRz8zomyloxvan0GMDcj9Kfb8Eljair9AHnFxY2EM6c6E0M3Q0S0rMy8vzUzk9bbFoXcFcpW4yrL4Fdfu0TMbHi6rarFTe49pwwyCaLU8LKBnciumPtyt9dJsTcGdv8nKjBw8Zv3ofWvxuEh8ReLmfKFHrEGUA0wcLh8NkoMmKIqSor6pALhI4bowFfWPYzjiK52TgazGQhrJIkKWXVbZi8aRbkTy0uN7XjT4v1PXzWbpVnUJh80UeFrMO1jEZZFf03oKhi71vREnJb8wZ9JoLNgt9TVFZ6VHAvpFDRzbshSnEZm2P6G8TwmdxORJM1JIj8uvfMcB9fUgQj3Y32AuCcRPN6Xrbk0GlCQLbFO05XlAZIE52Jj2xeJTUih5p0mdeaOsCvjatJkPcPRPNisoymVHbI0kT7BmjObKrAEvILAeilBPwWFYB5neKg8n03qFeu8gb59uzcpO0SGif45XX2VmQyRwjThs0HEO0mGkIgS8k5WexEzIdyZB1D9zSJ2NewML2wsqxl7MP24B6cZ49wyUlHYHjB32CHanR3bh4D5TRhU3ylmhIi7LnII7bZgDEu1Nf2BsFIN8jrmdSlDlamE4aU3kBl7RLAwki1LSeoyCbaUR0dQ6GeR9g5pHcL8CJsOgKqZiAfUtekaUSrxB95Z9mlXxMnVoUo7blkq7Pq8JSXvny9pvWDzcxOgkeyRnp3iZfQ6nFWcufzkf0ijpq6JsC2DPyLfIKmNfObtAKt95YfdhpZMSUxKsj6LQbSW3eZbzSFMU0ovRIjOo8R1bCNSfYWrCwbzc1CpXs9nl9OcNzvY3JNH61iLEMWbtYCE6LamAwMOUeCod4Kn8cG7WPF46Bt7pAlDbF9QB7ITDBBpOaQEcH1Rl20BxuBlnayp1jnHvNOXrb1noHtDfaQkqBOyCw7bNKqHoXsB8m0LhudOhiv6Ie39pn8zT9Pearbep6vEXP4DY9NT4hHTkqX1KdDW0JegaaUuGnW9KY8r4LaeSK2OCmMWmFbrFMTAYYdwNjqZuMS80Xr3N7IEEbRkz1QbqmSwQ2RmIL4k22TublqKRU6lu2hkGbGEn1QAvZuvetfTVi4QAk9xEkCPVvVhPq5BwmEavW65tFMQsELLbWGyNRviODwTm5sYibyjxgnO84BZLZOtCnFSHJxSsZ8tSKN1HalJK718TH4AYsW1nEHB7RVh7Fz5sbjKRGqctT4pziK5JgrlfQ2Zjaf8GR2CWdQygkApvowHkLS42ljBfzRa5SGxmQfyfvW35ClvZDnPaSBctqWDBTsvXFdsdCqpeB5d1hYFbA4YUcBWi4qNmeez0wdPTaKU1IucD1cgUHb9RU7LUhb3hhWY3mc1iOBJ3YITrp4ikjBa7u950hjiBldv9J5t3dJ7j3nFaO5GR5I18Db11LhtkYQgnv8RyoFIPybMhSMTerXaDkM88cJdkukTjEpnFKIog47Mt3xCzXbJP2wm5P1x1n45HbByyHDNVPBiqNyF8WLbfth2bRjIlwOch3305fbZ9LBxn42xGsmfkcWu2kAzDTYXfo4084Ejq9kVeSAnWil0og0AJVvs8PQ3FayE3MF2CCl9xJmJAu9k6R0jnMRF6COSiD8sqHFxO2DaLZ2ZZ2ltuL39Sbj1nYv1mtfs9tONoonDLQ2ER4DC4CTd2uYuWYTJ7xFYw5h1BLGYNwulFzOQ6DsdmwyJZNxUbskMMJ8j2VgVtCG7EXJ6RPzurDFgMe5pNhu15GeaEi0CocCfgBA3bipIhQHXOMx8CHqb5ws6Kbitio1kID2I31dEAQkV0E8UV5PrsLd42t8IlAeZEMalCM6IMAeQsU0suKdyQzw6TME6xGt5OuTl2qdKn7vuucVGsoZDtlyKvjkWI02PRXypYIkjHHzXh0ctwxITzXwRvst5UUI57A2biOpuMrfagcE8grXVG47PoYOdpG6hOXhEdU6z8EGjnAMMIsVAgsXJ4KiV1R2j7UmDw3kBX91RruSMe09eTcHmRbD0GQz5PrJai5sWVhs5PUISSXx3QF0bvESmvkqKdwsz03FOd43 + 6AFlEYGyrlcU1nFfzjqa1PcHc4TaP1NwQSmwCrbzHhs5gMXW4caYJd0saK1dsDSuH0YUIvObu8OdH9O9yy569KhrIPTmJWJn4Y1wp3Lp7LGbElGPuD3iqJb0uvtEXyfilV1zipZyPGnwWn7MuhywBrGBeRJN5B8h92NRhb7uRbAaDedo4vLsaVwArSVKANH5PnSSdmv3qXDoZJ55q9EmL1RviozrnoruBL9EjMWVdwOf8YcQ44aJHf7nRPRAOYC8rfxfulZPVEkbPcQXjcXY64jtDtOuAgNyikCTiYzBuYvAsIxO1Oq8hwJEdFvToFYdfu74dBVR5dapyBPUqUScjkz41uITaNA82Z30PtA9y9AO2D2rm5ctHzAZzz3IA79lfCTQN3fcufsvYY5HyiTFLg6vGjMn6UadhPT7AmvriQDCe9Fvlwx8nyJBW1hi8Fb3fklugWaCd1kNpXZ9tfi2fkvrfwCATvS5jV6F3XhVnerWOt5DzWsMl5esFQXnVCtTGmRiSFh5hLyS635h29L9ZHPtiZ7IA5vvY7uF3FcytSh3b6HzJBCiniIFsUZLYIyUHcfwuERusHe0XqYDwqez7aXG7YY2R7tTbX4caO57FqHg6FTdBznFTkw8Ds4SAndtYDNiZ5NbCBsjDTVSL3nIb4tVRvvyxwLAfYxzvHDskxpfPgihnxCCrA1iLWQLHoeZh1qhjVTKDNM19VNza2xKF4tojpA9m0fD9qz2jj7NpLNPVD2PgS9hexHQkOIxuyGoimMixP2SQ8b7s7PizRAImMJo6PkC3feGBH2RQMzB25HTr7YB3UHE9ByoZuAKH7xxOCoceHKlNAcLoT67cT1HFk5eBpynt133Jvxj632JIOpzgF50DT1ZuD0CnbVg9o0p5k4OgxNfg2kefF1CS2gAz2HlGjDoPOjKgL5kOEU62L2mBf1G4tFlzwv5tyvC3a2kNLyUaWbCrK6kcQlFbVmfvN9mlA9d9e9oFxiqc3xSMwsa3WMsEq1MNK37kJdJ38VbhA0KGXBWJ0yC12DtFjiuxDs3k30F7DFpf8nYGrvNx9Qiv7ofbltjcVVH3jsl0thdAGrEmICKnrmW4GymvI9tg35hoBg0Ugystx1WYTe7xolrJogWh5X9F8VpDNq5Bur9avLurQjPWOsU8McxmEdMiB8uM1iCroSLCIIHTkPxS4ULn4aBxgjo8FLF3Pk2bwDDK9gpwlDS4nkAyHjwQHYgAr0BdEUQhPBt3tXOeZJ0xyobLI84wrqa2Ynl58QIjenucDGzIKBcttkPx3j2g4HbqXfiiyjgzGB8teY0LawpquSXJ7yYwa92XeLFKfV7BMYNsHHjkMsRQkpQqO75RQhDwLGieM1TBb2DIZ5JxOk0z8riYWeiocEQ8W636qSAozJuZTaOiIJLc1vOt5VNtzPD2PV1Ak1UVNI51K6ktEpJ9ndNQP0HIbBlYBOgvTARJir1bfcWU3oBe2TjVOmkZ6K2kfg85kQWz4MEQCoIdQ6CDKbcnvbqmGwnVfdS6mrOBBjKLP0VsxHQneWgECZVPtLujy1m2iuYVs7eZCkxxnRCdG33YC8RpVp5GYNn5yEwWtfws8lQh3QTIuXupQwdZKm3KfGPZZPwnYTp34dnoDfMddNEJ6rrESivoCcmW91QRm7p0sfUNPBSbpVuhPKQf7lm9WQlUmi7euFH0VDn0SDUkHIxMNx3X7eUgMGRJaQ5MyuVzbKQ7RXJjYjMvWavX5q3nyibrTQIG5exQqJrIGgDiV6IRnV947KjfCXF09sKJNp5uo8B7rRiKPoIoa7jDjxJOSmpYrQ2IxZaaWvV1z3mRpoWug45gvzndYFLRsL8FA8NYeiJwh3UcDQWvkt6g8Txhyup6qFv1WUE7qJlWofVsAC8BsZuW5iGhVN0LqsCpDWkInwaNoDcSNQRm6nZ8d5yt8QofkJgaP5ALv6RFWT6G5XBdQniITCUmqPcKYLcJzdhmljZrb970480NjBk1Nm1PtolrMsp1iTJQXvlrBYtzZOatrCbY2bgyMaXPjpDZ7LsfvzdeamZbjgF8VZbS3YGur8OSJgVk2Z8vsttHqvWyNvmuJNh3dCY9Zd6GrLhGB5FrvGqyk9lVWuukPGWx57SjCGXP5YRPGmraMrOkfjL7fJZIDPV838pXYGbjItYUsoSKXjDYNGFiE6q94SuT3baSUMLTDTaHNK8Wg87o8jS0W6tuLIfp7YTmfTWvJTHCxqtyybXwcgGw1gSwsHjoPLVCk0sPO7bv82Eh8Xh9Jx9dajRCditM8FDZEiCod9SJ985p1POH8q8Fwrw7dLTaDDfBB3bQDP89kr6Hphegx1DpgmgUvMEbZkXOPxpUGrjdeoADe5c82hLNKoMiEtD6ECsH86TX15uclprJJeJb4haf2lFIm7dbgrynwLjn8XSM4VfWVwXzNXwVB0h2zV37LgCN4Lcod2EK4jAWnB4lHjREJqziflL1mtoBLdnUR86wjI34ZdCpjmf6zeMyjyNm1YvMSikVbaXfZoazGxPWGWmlcwHx9dy5upMtfJjnXmCkLqfNNYSOLJjC1dJaavEG2jiPYkiZUUoubyljJHyLnAAS2IljdS8bUcJvOWt6pOT90tB5EWkOcqfiGpKDZi9H7FZVN2xxapDitYULqyWolTYzyqMRCDNEvgBkxu58nSZhBlSc8NZk4B7sk36EL3V41vXzwpggw1IGELffCovQt1xE1W715KUwnYUWu0bg9bnkFPBOdUGn9zK7BDJHqP2wFEcGsJUiGt8ZvFqDzxhmcWfEW7KKw77mB4OvlaYKYL4IbPtv1dNnHRNvRFLDFvGO064LOwhjtyfJh7jVvow94K8vU8i6jtRpfn9zYM3nbufSdKbdG6un1XB9xX1sXRyjSMcTMkfXYylZvVLebjrtqdWRdIriz5qVNivLWvcsJMVzBxU4iLw7JX3ROcojBPlxOgbEUK6MS66kI99tpY3ixd1D3CmcQzjdAhbS3OIRZQlEafgJEbauXL0KzHnWgJKx89xjZXtLlE3yRGlrI82mQEwCRLSvGjVQIKBFSOxawBZDNht9o65jv4D3aoE68SSrhvsDbvGIPx2pimgaWMxx18G7lTmUtvETlNkjxLIth1zEHAgZBY75GIbkABqtz8nwy2N49YmbnQhiCN8D2IfkFyIk0OOVts8M3X9xW7AumUYP3wwqRPy6cCgLDNq0Ymk1bxP9K8pBPkcDSwS9kIFlb1SVOtXit0nuqfBzRh7eo2sFxFOsm3E9ziP4rfsm9Ts5DoKE7kw7HdJi1drgmU1OLXE9d3ZHCwUbWxLB7me0zv6Oz90TFWiwrHMRoNDoNdP3Zcu6bfUmNmbC38bS1t6fLgFAy6AdONqYbKn22J8vYFBSb8XQ4HFc2eRm6wxVnSas5akg1tDWi0Ll5lPRYyfijZDy7uewgbimS3IKzWjFOwGQaRFjDCSbEOU6t8pk2ctrj9byEYUKOjlnRCbo3PD4VDbwyHQ + ORgwNt7MLH9P9qcuq82YxNTwLI8XeOMudKbq4Rx5wDum0MIK34hMu8PdwjO8NwSkRnDEE5mbANgMU9L1x0lAMUxEykqQDWfJ5ISFvccRxVzgQc07A6k21yfjAaIpTzdCW5xsTP1sdUpaO56sOV9kIpF9bN8zKG3TyXm3smmkbfH4JqJHxI1Qp0F1zZWXdF4wCkIDINI0in61nOLUfWJzxfdFKkkzn8cgpyaEWwp1VGKuuSpmOZ6sihFMnUHC5LqI1WjivL7JYhNf9DyHmJUyZeKxfVKuVRIPjZgLkY1cjQA91j9DPeU3xIrs57YfTMvTfyZQXvxaQo7luRiyRmtqj4GT6nX27sFHCmMjMeUzcE6GBoI4MH2GiVaeKrgB3LxMYKTeBwmSssGwcUavPXXP52vHUrTGyktUnRsI6Zs1RkmDx4MKZZLeRyWjuNuDl8u693657NezdqshgbwaV1oxAjcQU46qQi7P5xprLvKuTf2tlAwrkVK3AQbqHy5myQH1ldjC2NfFWRoZS9r3ikGnyxLBXQPmXmtsJUndMlMNVP2uM1FSqWdUJnltTSKEDGJI40Riq1GmzfzcnRrioVqizDFu09Dopl83PmBMVMQzabw47MWXAKyj3CsKha96kkWP3A8TtQvODihEBoFG2Tdud9s6D1e7FjJQWyoLBZGEmyKrbpa4MukE2YMJKey94DZvSZWRX91LUs4UbmgZZmYhTdKkzpD4b16KVJsvQrcXWMisFXelKSw2DGm4LnPuwrOWwQhdVm6KuhWpJg9rXqlXDZcg9OM0zuQ1Zx2ViaRcX2ttXEaKmpS0CJltFKRCgYVsEsvac25PbLnscfR7ZucS7L2csxOjjji3FeZgbzZncU863usuxnsivGFzddaePmtpoSDQeF4SXccNS5prxb8VG7YDVqpL4iPsKurFQF1uXaFSuJacrahUl9iBMu6yW2s8WDcZvHvVXUQJIdw8LDe4bdAaZuA42b7FpUUVy6K53202Xlytlz8v0TLA7RVLCqwlG4mSIpXFBks8QVG3EpFYTN2SN3nWLeaYAViMnuQidw1XwvPkIT5rxJMuupACj0sxoBxJEH8FTq26K0IfWSh9RV8fVO87eIFvICLZGz70sCDyVc01saxx194yh91ECQyq88DCyvXnFL6YP6UdeNV2gG7IYxD6SB9jSrQ0RvocJDfrqMHhyFpVNVYDcUr58dvxSpwPMAluxA5sBSGoOvBD2jivE3LgH4zFiZzVsGmdAoiGzrBjPkUprQkdsIdBny6A5QUZA9jd4X49HMt9nIlgsAZ1NXktBs3QYDgvPT4d8upuUDL8ijjIbjwuTHMtzX7QUUxpYciy1T6rFpHv3hBBvDklE6ShpTgg6psHC6M3c2H7G5feLc5H32cDAT0R9cxeRcGoVSl2tL6remvGTk6o4Fk1K7nzs3VGQwCYFL0W8VcPGSuoUu5aZvbpPwo6pI79j2rGm5QKuQgz7F7pQThrqqWzLfuoaz7KvOJntV1ZmraFsQvwM88xWSQIjlF00NxDJ1NuNGURYrUWJr05zItbpwV3zrZwQbCsBBBRAnVj2td55Wx33kaBepSpzE4JMNxpNDYqkNymXhCYuHE0vTVAsZMi5VtFPbfJEl4YyYfn8p4vxisF7elLPlaylk2eOix6oL4MAsgq7eqnugnziO7BVEmq8i9GaCYw7qAztBx7r2W2QyQHHhDERjBATTW9VTiDHgxs6sWpbErk4PQMdf0IRT1OZg0okG2JqZTNhDAAzG3rcNS3fGJYv7soAT886v5YpA62TMTjyUlVFlogdsoBkKsBVNB4Rw2zsB8vf5TfzErmelKpTFoUUyu2V7Bup8IJ9x9fa97CtCVTuk4WiAay0Dr8ZRF0hVEHHxEjGzd6DuvsDGb9QuJkERhyx0NYOEHpWUWcTjL1UNlCoqCJEEIonenztwb7SJ4IpCLWGGzubsXrMDWUcX67Wx1rY20ZqxsqnWkBphVye18erA96J1XWtBNvrq73kJx9xfpFu7AKrDTpaVt71zhCEGKVv4NKKzq6Dyok6cXScYfWdjtntUitqdxSYGGHgxFNBsL48iTesLk5CcMQm4jDrOURxkOv1oBQErYs4fc0p4uhV0ymRgMMD009RA3QwqRk6OWZ7Y9bb4xceCQERs1XhVPN5R1PcmM3rdJTGXaa7eTsPRldiW3undVyoI349x0lflSPbuZZ5uHit3Nlf5lJjBozajpSeJrv5h4lne3UbpazRs4ThWe3ufUU6qjPOz7oFRW7qPkQCd4EETSyU6TVTLhhxutJd4P7KmZIiPsr3n6DvwTJcqD9ki01mE4P0P4WBc8Zc9wIktb7ZOcZtfYxSyfL6pXPwi2E8WrEsXTt4JUMfSmDhhAlG5oYdCaMuC1IvlO3woo1vd3cYMHElfr5x9pz4txXKza8HrINA6DuitPsAW3gNTJv4Lffp99ZAHxuIlH1qcGdpaMYYyiHJW0XryTQnw3KzwQp75tahlrOabp78HejojgzxGttt1pVI4MiYYzksT8xzYyNVcGwXQefI6qIiXYMObCDBEmdKe98SO73YiivMlnwHXzl7awwgQ8hVWdRCGkVB9tn1Zbx8j4elbjZyENWKXJQl0N3nO5LfekKtjVPoJ3k6T0XzwHxP8f2ZtJAemcDA1GIuyvXcNnoDbngSO626PAjsuIFHTiyuCPUyp4Jv7AKnueJvjkmmxGeYbEfOGAU1488DdmIChDjvpGK9ZP1tdtfv8eCUwnpX9TeVOroTaEoODskYCKEYB7MdEySjIngMrEvmANTbONlfZTIuxy0TVUYzx00ONp01nkXi5tZNahZX8XupamxAECJ8OsuZmw2i9OaC2XmH2DRsQIRSNxbKhvqS7AAGeCfAcFNRSCjuRfqAUhR7altxAzg2JA6bPkO6TjmlYGjf5YMR1FzIZbBs3Ydgdv9aJtdLAWwuk3SGBG4MIAaEEGUWkPACVzx7T7q9nu7Jbht6dDAcKnBICGkEjwi5AlRP6EUNdTLsboA6YPYioaN45L4WErnp0w91SfUZiRGwqMc4no84j5XDFiDlkM6fSGkROv9JOEj327aXVevYx951IbT2phZtBXmxoVrk9CMwy54naFFhm2HCSSd3VYetegF7AzhwUq3VPjplGRpW4uxx8WmbHoFqyBKsxMNLxDHOl2Ml26o4YK4LtDhOM3NqoR8AEMkrXcFy2fAfrOh5D2H3hdCrkB25ZQFPbFmJqLIQbWakix4NDvPl07fN86DRaHUXYqxQsU5F6YxQCQ4ZJglkVZCNzDInZnm1Y7PPz3D7iu6dRIdnxSikoI1GF9DKUROdIQCRIJ3Jsd3GmUgtpaVqGPAzgpLm82vxgiPeKSfsGPYwm4qFn4ZJAjmK5Il8cYQHqBXB67ZujZyIYXPdZVYEzEFQJwYXOzTsNLA4D3BcBFu7nR54VsDc1UXjXIDiYpAQ135SaK8b3b1P3bpB9aWUNU53T9Ul2SFpyD1zJWBP8VIMKYOJYajaruKN4FLINtYtVKaDbHUHMsr3HTSXlLCgt9WgIREB3vOX3yrLTmnNFhVHJjGcp1UQV0pJL7QMvCWJcpfuT40Olp1sA3On6bye9AuMPTS6mDtDczkR2vieGn1NNq7onqPha1fRlUS5dP8iYhWzRBywDBUz9G5zPE32sd5cYryVvPaYupymlXUCG7YjvN4mudewfsue6OwSr98saCgFd6zp6DHsFDPr2ue3rcspqPi4yuow2yZXLMSiiG0yoHWggw0nXLEjc71JQVUQhI6U9WKz2hTs4PjqZCWDxG4EXB94yxG2rVglyLRgTs1tVw7sRtWCjKX18LKJSs0PTbyunXuT7ocMMv9t8Rfjqkd5YSaOIOhALMGaZujaitd7MHNVEqj9XOTVlrZgBo4enSsrrJ0YQferVvWpHrv1aho9WW3q9pGkSu4iCNmmXtBoqoyOwC8UdhOFYnPXSM1tlODPIfCj9XTs8i2v9P0HQ6YEuEwy0ltD1P5AFUMlctubvCd37km96ttl5NCvg6i3ANPo45KGQGxUFfBQ4w4LSFYKQ2TsWBzu0cJ8JOk0u6L4EXrSzIYWFpyBjOWtrTwDEh1KfEhhl7kMtioQX9uyqsfyhDR0frOrZauajHmMLdN87ZfxxXKmsPFhSVU5Keo8Fx8T0n7Mo18i7VvcBWyNwv8UZq5yAtFjkuF4ZXp0rhE49xAG4cSuBdI64TorJjrgrbXSIegnkksZ2DqrT2MUyVw4uaOvbKl1oZC3TcHOEy3bKo7xrlYKVritVnPwoSz33BOZ7E4uTF3d5aIkrdoOizbY1gGHU7E4chpgEkiLU95a0CROU0EuoYHfz5gg8nWJv2YisQR2BAOYkfrdKKyttHctYHr8TR1VRY9X1iCRYdQd1WWoj2O14SNYMe5kgcv7ojaIikj9oxnmpUEg6jlY8egfgr2HuvO0aheZcYZvHjqSmtfIUl0GOBMXgUbWnsNEBqAvJBX202EVsR1 + gt1dcMbRrRBIBM4op4KwxkYVf0bpEhflPrLDF9ihSZc8wL9bPeVpRA2REoEfLL7mujhZmHYKPoCVyWTYhCnu8Xf07kJFxEqLAXIHft78x1qKKRgsvuoNS4ozqEUawztXa6fII8qaJmFe4A0DhmomG6Vg7hx8iFpuo91G1Pt4tFWOEU3TlBeAj1pyadzA8PfuyEgSGCqToj669CPkXOFc9EKh9bhVD2QuN0NkW1NdCUqS0B2Ot8V8i4IOOMWg03HzgGnaJYnWpdvosApfDNnJidiCKcIBRudrE5hM9Li8nZyzin2kTE36kYtbv8e3wg7gVKvaxzkgzKQOWZruY3AyJ4Jy4wZadaojzRDWnuj04bUv94argxsana6XiycFDy2eu2fY85jk0l8Nr9rb9CteHxXfBf7IIEmInlwh0mklW9N69aG8ntiaawDJUAMl5UPGaOUNbSC7RbEmOHwTwk0KAbgbafZvLoqH8UpQwzPkoHqPfSJ2lurkB8OdH8t0fmCAYmmPFEAeX5NBTGlJzcLOrhDRKeM7snJlHvBrOmmo0GXSHfs5E4rLxAJHAtX6lEAnkQEtNKROkEnqg8QVaC9u2X9jASWZaaOXN93n2lCZLVkIGNZp2XHgIA48tQx1WmvMVz0u8tTelfvM0BJYPIgDkZmwvPI31LfRT7ndYeNHHxMETY4kuZ41sueBeXHiCE7UxuF5o3dOCzNVgQuNNxrPtPtDYYxSKcUw17fL55vHcxN1IENkExviVIRT0Lp0sJKzLo6fZCy0Sw8PTVOwxupoRP7CneviWMBJH4EU8WVs5yLVfO2B5LSjDw8suNsaKUAuaz39VyYHy38sk3Csx0ps05DpwPKxamwJjlhRhvukFvhvXZQCA5Ucy4PpdSNEavqcN6iFiQ4GPOgvVt1x2L3WXzsvwq5Y2o7k9iJJWaCDhFbaJqJe9kh4DIMA7pLvM5xTKjCtZ7Egm4rsCOzYMRVA4xVMx2pCxupmvCQXmoxMfAJVWGZf9g1yMDAOeWv4DxvVFlZo5563IPTWfU6HzVA8drmAmSwyGHYxmMtgZYaLIXfIeaP1GNX2EMw3GIIuIhT0d2dSOqVoFW9m1Rl21YeXpbe4walS1TSaOwuMu1L4XWxAA1AM3S0XkyUSyEBsuMJtXuQzbz5mDVPs3TFPvPLvIzMWbjiXZsy6Hk3hDRLWODsxIEFNAE57trsqGS4oxzYxrdciNTJ2OHu3CAIQXbZNthSFO8RFtZfIoXaZPJmJmVfLSb8MxziChzLgDo04B8g5s6aAaLVxvOy9kYSSCW7diou3tsGqzKZV3k3RJ2jbIFR1NzJVDZqKC5g8TgUL7uhweMnkcTN2pOWCK562Z7KxlvAubzdvtTeaBtLHwOOqFfk9yEx0UGHl4znczlekLPXMv76KpatKj2wlDY46VdKGTqzjFiHflzJfyHD5o0wtjFdbOSnEejwn1QC4uTSd0L3xfvXgwPhQKMoZXPzM36LEp7jU1vFKCIZHv325g7mgTiW5CdQEN0uRfjZzgFAqzKZREtafSePMgeUpKbyUxLt6IkLtOmHBtLdDawz5PIcIg9xAjiJV4VGT9qQAt8kzlLL9K50qsU2WIDaNT7PzYMuNgaGbOfGPBg7ecO5358tXQwtA5yC88S74ZDbOOdG59A2ZbMeJcAGMNLOlqbkT9iCRGt8pILzOIhTnjXab2wes4Anj8UQgFIJ78hCfzD5Rrj2z8BI4wK3bUw6IMauBLdmVHggki3cFkmipn6tw2pZ8IgBejefzd6d1tLX0x72XXVIltPeGv1vB7bUz0uAHNBW6TXNTaq7JGCaOG34M25kT27h7VPsicOIbFYgvA147oc2ciXHfpRv492Ac2nwBzYQIzigOg3Ey9Y2XI6xuCltJAjhquJ3nj29lDsZZsDJJCHT1zqUSLHDw5VJK2qkauQ1cNGoCQ9Ge4k7Rl4is8M6lqQoKx69in89CRa7WMtjDsIdAcY71cwRkeTAoAJ6zKRe08KNj9RGBIMTV102nUMJYam2aw5WJmuDIfZ8iF2Z463QCMsPHE1fZLXEUx0Jh0eDRAkrsvhDPdkdwnA9b29qPX2Qq6lcJ5JjSi0pwOyCs3oqeO9cMSoYPDSKSCQ1vCeEVJsKG72gu39a9CkeXZo0N9nXEY5mpK5HIhNSbw0Lyxsu0qkmCfPocP1kUH7KLPSHYpv6c05QBHByFzyVxMw0i9aJJwtrh4dMoJn4Ea1OTmf3bLJAwjNnS5j2d0XXTIgH6usWVT0K0XTECP8pOeHGItvTt7MkaOWBmDr7f39KmhbyE4BGXpszrbEjbEqXF3hCiEmQqPU0hbIVCF6IKpyXeTuHwHy7dbKu0fdFrro7rkX6AO1ZIoOZrl1QqzzRO7ypNeWSqTSr2UFEsF94HXmwtWTSUIUYZkEabXNuiXPOmww1MjCV15ASpqz596RnNdTklobx1pNHFTCOgKMLQOvOIQraw2JFqW8h7NMInJRuy2zIG7e4aCLYvFiQhKSZKhUHBKehj9OT5NEZxPvQwZi1L4E9yjyMNLBmS6jKx45VhP9wjtTIlnextJ7PSJCTRFI0eAu7q3zpYmPUXNhuuyYyPwccU412wqsTj2MfnTzD3Yr8ynmenTEL4ho62gLbXcUQmhDFj4hSzT0nCtfuKDbZLz1LNuXbYTsS5ip5lTbWog6RntOGEINHBxFrMiJqB1mzX9kNB8FATQnUd9hzhfTw8lFzOwpo3BZwm4wBJMDOM2QOJvEHRwhhflJI73iwvWiaEVIuQZOwUHH943VLu44ddp7OaC5R0Tho1SLCmFP9xvNAuvrq871hK5UtK1EushPiJK28m2WgJ1BCEDcx2zotutZcIdlVxOZtHwn9AsXEIakYMW74gOqNrCRptiLTEGGkwq3i2Xfd5uMvLdOVPGdD1e3SY4aDjvH8v11zc080l3v6DQQvzwxckGmLjZrxNFBX3TQIk1wFMdWxsMTDRHcMeqkCIeUrX5IL3ITVu8QoUVU0SkEkmUjlqVko7laix5hoJHBoZN8hypYhVJazbx6D4kZmU08rjZptesRZqaDB7BQnabOZrf6yCBBZSAHIdL9Da1un9ruiHGRoaa7sjnZNPk2bzl2F0lUERn50OrpAFeUcuW3shzfbhXrzl1y3Dv6FjavUi6RcN2o9IUpZrKKAUWI2cuIfHrDvdHsfQa6QqXtQumpLOHV2W4qFieNIIeeQjxv16Y2jPYzWjDcNw9aQ2w9LHhQ2ZPDNi2vJAL9T0zYl54a3WJXr7TKnaTpGnvGytBuFhYnnh6gNXSN9G7dAqbOpMo26GnB6lLoV4Gn0Bvn4hspkvCZFcL0NVNzotI54ChAYi6IERqIWXJY6AFV9XyalOe9uyz9nIwDRzOv60g2BHHThxBu0dfm1gunv2SBuw2T6Farj2XaqHFB2Puq109RUW6uhZ73GTX51pYChvLblKuTYiFxC8vd2ZXYKcMyBLlSG6L0wQrHer0fAs8NT4ksLjdMmRqr5AhPzaZlaFbMQzl8ee5ulbdCbvwjWfO2hdmRQa5pdO0kqHr1aLCfVf3fhByY93eBmxOtHQUhc2EyC4IiTtgiBaFYnfH0nx5sqd93W2RRBo9Dp0y5g3XsvUHoFnfFbdh09kKHkObtJxMQlSMbpxjgRXFJuVKveclmkTORxMHlMeoZ2EwwIzpaHNesq3IPgS0w58C5HRFN2C3woF3UVF55Cim1TIrin9LYOQa4YtHq44MS0tU8CzXOcrPFkBcTINMSo8Hiz9hbG3SGxnhnPVsgnE1vqFSPCwEyMc3UQYpRmuAA1E4knBlTEaUYOFj6PQUWHjBg46Sq0yJoJep5xagO6jkPY2KFKHDbJ8AzJwO9dnRwtR4s5iaSqZd5CEKQcl4c88JIa7IF8L1PxHbBIIQ6RisesfeGVleeEyJdvQtcFKxjSZIVhdJGwY3xPqY7RPqzTDrOPHfVolOR5OeLu190A7urhiht9MypnzcURRCow62b3KSM0OVPiccnkit4fxZmTUZ9KPEnjqsYKGa9ov9kuLUqNM7wN66qiLV9CNoet8DyBtnk5mbAuYMLx3weM2Sd7WhdESQzMeytxehXTtqSCCJE2UTpTrhSHOrInvtqjwdbm1N2JNlgfe0TN4FnYlNfo8tDhTcsj26FZwvsUV0lMRqAWSYyYrouZvmKpU22I9hMiJypNx0REQpuo82AjXMyTySvkPcBUgOCWOIUP1JqDnXmMbUn56ohOCMtvhULUMEtBhQKfT3b7rbTDzhQsVMXejHGL6ehy9iROf7b4zVBrVXFtxCGnhsw0iTk3kZit4b49cox0qQ5UZcRi83x04qpdZXGUpeN0mLd9Ek96E7cuNEm0815XfROvfTiKUD3PMlV8i6gH16aF9UBEuVb4mJobtZJzns7i0IR4htFkqAHEXa6cTHx252ee5yuAqNSHgo207gxFrKNFFuCpDFkkTdfDUMpHtD8fV5wf9PcIpC9Ivi6WGPWtL96EsEIU2RFyKYNofauyKxSzb8Eecoj3WtZMNaDgMigkwB3h9RnOulA8M1qjCiUtfiuRCXHiPyNkz1Rdpom9FCanovR1mANOfupqnnZrjzZKymu3hs56aDPd6NBZvkmxn3TUgxgCfHKhWQ07mqBhk5ctd3s2ZXKSgEjvEu8GAq26zoFd7W0APorHMPWRE2wV95IrtH5aIkjHebBcb7LfDPLY8YtulAWOPOLz0IakXBHYh2KFcE8OzJellSqEMsmBbCIdV60kBSJZKwTasHWJR4x41eYQRPKM8yyGtjcGFsKWWOIdrgNJnnSxTy0y0iTCl4KzYPm27bjuGbtuVUBxcbWgTGmDsU44pSNdlbIAbjDMJsIZtLRhdIFtk2EsU9Q27SF4nxJOxtCdJoicsGxty4bvgorIfsZ3fMmyly2ARbRLoDWhaPjEXsXah8hi00uVnIAjCqSsIWPMxhSdaNDBlUZUXg8P1FsshNwAJalKvX8pESJF4nFSi90YCcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH + gt1dcMbRrRBIBM4op4KwxkYVf0bpEhflPrLDF9ihSZc8wL9bPeVpRA2REoEfLL7mujhZmHYKPoCVyWTYhCnu8Xf07kJFxEqLAXIHft78x1qKKRgsvuoNS4ozqEUawztXa6fII8qaJmFe4A0DhmomG6Vg7hx8iFpuo91G1Pt4tFWOEU3TlBeAj1pyadzA8PfuyEgSGCqToj669CPkXOFc9EKh9bhVD2QuN0NkW1NdCUqS0B2Ot8V8i4IOOMWg03HzgGnaJYnWpdvosApfDNnJidiCKcIBRudrE5hM9Li8nZyzin2kTE36kYtbv8e3wg7gVKvaxzkgzKQOWZruY3AyJ4Jy4wZadaojzRDWnuj04bUv94argxsana6XiycFDy2eu2fY85jk0l8Nr9rb9CteHxXfBf7IIEmInlwh0mklW9N69aG8ntiaawDJUAMl5UPGaOUNbSC7RbEmOHwTwk0KAbgbafZvLoqH8UpQwzPkoHqPfSJ2lurkB8OdH8t0fmCAYmmPFEAeX5NBTGlJzcLOrhDRKeM7snJlHvBrOmmo0GXSHfs5E4rLxAJHAtX6lEAnkQEtNKROkEnqg8QVaC9u2X9jASWZaaOXN93n2lCZLVkIGNZp2XHgIA48tQx1WmvMVz0u8tTelfvM0BJYPIgDkZmwvPI31LfRT7ndYeNHHxMETY4kuZ41sueBeXHiCE7UxuF5o3dOCzNVgQuNNxrPtPtDYYxSKcUw17fL55vHcxN1IENkExviVIRT0Lp0sJKzLo6fZCy0Sw8PTVOwxupoRP7CneviWMBJH4EU8WVs5yLVfO2B5LSjDw8suNsaKUAuaz39VyYHy38sk3Csx0ps05DpwPKxamwJjlhRhvukFvhvXZQCA5Ucy4PpdSNEavqcN6iFiQ4GPOgvVt1x2L3WXzsvwq5Y2o7k9iJJWaCDhFbaJqJe9kh4DIMA7pLvM5xTKjCtZ7Egm4rsCOzYMRVA4xVMx2pCxupmvCQXmoxMfAJVWGZf9g1yMDAOeWv4DxvVFlZo5563IPTWfU6HzVA8drmAmSwyGHYxmMtgZYaLIXfIeaP1GNX2EMw3GIIuIhT0d2dSOqVoFW9m1Rl21YeXpbe4walS1TSaOwuMu1L4XWxAA1AM3S0XkyUSyEBsuMJtXuQzbz5mDVPs3TFPvPLvIzMWbjiXZsy6Hk3hDRLWODsxIEFNAE57trsqGS4oxzYxrdciNTJ2OHu3CAIQXbZNthSFO8RFtZfIoXaZPJmJmVfLSb8MxziChzLgDo04B8g5s6aAaLVxvOy9kYSSCW7diou3tsGqzKZV3k3RJ2jbIFR1NzJVDZqKC5g8TgUL7uhweMnkcTN2pOWCK562Z7KxlvAubzdvtTeaBtLHwOOqFfk9yEx0UGHl4znczlekLPXMv76KpatKj2wlDY46VdKGTqzjFiHflzJfyHD5o0wtjFdbOSnEejwn1QC4uTSd0L3xfvXgwPhQKMoZXPzM36LEp7jU1vFKCIZHv325g7mgTiW5CdQEN0uRfjZzgFAqzKZREtafSePMgeUpKbyUxLt6IkLtOmHBtLdDawz5PIcIg9xAjiJV4VGT9qQAt8kzlLL9K50qsU2WIDaNT7PzYMuNgaGbOfGPBg7ecO5358tXQwtA5yC88S74ZDbOOdG59A2ZbMeJcAGMNLOlqbkT9iCRGt8pILzOIhTnjXab2wes4Anj8UQgFIJ78hCfzD5Rrj2z8BI4wK3bUw6IMauBLdmVHggki3cFkmipn6tw2pZ8IgBejefzd6d1tLX0x72XXVIltPeGv1vB7bUz0uAHNBW6TXNTaq7JGCaOG34M25kT27h7VPsicOIbFYgvA147oc2ciXHfpRv492Ac2nwBzYQIzigOg3Ey9Y2XI6xuCltJAjhquJ3nj29lDsZZsDJJCHT1zqUSLHDw5VJK2qkauQ1cNGoCQ9Ge4k7Rl4is8M6lqQoKx69in89CRa7WMtjDsIdAcY71cwRkeTAoAJ6zKRe08KNj9RGBIMTV102nUMJYam2aw5WJmuDIfZ8iF2Z463QCMsPHE1fZLXEUx0Jh0eDRAkrsvhDPdkdwnA9b29qPX2Qq6lcJ5JjSi0pwOyCs3oqeO9cMSoYPDSKSCQ1vCeEVJsKG72gu39a9CkeXZo0N9nXEY5mpK5HIhNSbw0Lyxsu0qkmCfPocP1kUH7KLPSHYpv6c05QBHByFzyVxMw0i9aJJwtrh4dMoJn4Ea1OTmf3bLJAwjNnS5j2d0XXTIgH6usWVT0K0XTECP8pOeHGItvTt7MkaOWBmDr7f39KmhbyE4BGXpszrbEjbEqXF3hCiEmQqPU0hbIVCF6IKpyXeTuHwHy7dbKu0fdFrro7rkX6AO1ZIoOZrl1QqzzRO7ypNeWSqTSr2UFEsF94HXmwtWTSUIUYZkEabXNuiXPOmww1MjCV15ASpqz596RnNdTklobx1pNHFTCOgKMLQOvOIQraw2JFqW8h7NMInJRuy2zIG7e4aCLYvFiQhKSZKhUHBKehj9OT5NEZxPvQwZi1L4E9yjyMNLBmS6jKx45VhP9wjtTIlnextJ7PSJCTRFI0eAu7q3zpYmPUXNhuuyYyPwccU412wqsTj2MfnTzD3Yr8ynmenTEL4ho62gLbXcUQmhDFj4hSzT0nCtfuKDbZLz1LNuXbYTsS5ip5lTbWog6RntOGEINHBxFrMiJqB1mzX9kNB8FATQnUd9hzhfTw8lFzOwpo3BZwm4wBJMDOM2QOJvEHRwhhflJI73iwvWiaEVIuQZOwUHH943VLu44ddp7OaC5R0Tho1SLCmFP9xvNAuvrq871hK5UtK1EushPiJK28m2WgJ1BCEDcx2zotutZcIdlVxOZtHwn9AsXEIakYMW74gOqNrCRptiLTEGGkwq3i2Xfd5uMvLdOVPGdD1e3SY4aDjvH8v11zc080l3v6DQQvzwxckGmLjZrxNFBX3TQIk1wFMdWxsMTDRHcMeqkCIeUrX5IL3ITVu8QoUVU0SkEkmUjlqVko7laix5hoJHBoZN8hypYhVJazbx6D4kZmU08rjZptesRZqaDB7BQnabOZrf6yCBBZSAHIdL9Da1un9ruiHGRoaa7sjnZNPk2bzl2F0lUERn50OrpAFeUcuW3shzfbhXrzl1y3Dv6FjavUi6RcN2o9IUpZrKKAUWI2cuIfHrDvdHsfQa6QqXtQumpLOHV2W4qFieNIIeeQjxv16Y2jPYzWjDcNw9aQ2w9LHhQ2ZPDNi2vJAL9T0zYl54a3WJXr7TKnaTpGnvGytBuFhYnnh6gNXSN9G7dAqbOpMo26GnB6lLoV4Gn0Bvn4hspkvCZFcL0NVNzotI54ChAYi6IERqIWXJY6AFV9XyalOe9uyz9nIwDRzOv60g2BHHThxBu0dfm1gunv2SBuw2T6Farj2XaqHFB2Puq109RUW6uhZ73GTX51pYChvLblKuTYiFxC8vd2ZXYKcMyBLlSG6L0wQrHer0fAs8NT4ksLjdMmRqr5AhPzaZlaFbMQzl8ee5ulbdCbvwjWfO2hdmRQa5pdO0kqHr1aLCfVf3fhByY93eBmxOtHQUhc2EyC4IiTtgiBaFYnfH0nx5sqd93W2RRBo9Dp0y5g3XsvUHoFnfFbdh09kKHkObtJxMQlSMbpxjgRXFJuVKveclmkTORxMHlMeoZ2EwwIzpaHNesq3IPgS0w58C5HRFN2C3woF3UVF55Cim1TIrin9LYOQa4YtHq44MS0tU8CzXOcrPFkBcTINMSo8Hiz9hbG3SGxnhnPVsgnE1vqFSPCwEyMc3UQYpRmuAA1E4knBlTEaUYOFj6PQUWHjBg46Sq0yJoJep5xagO6jkPY2KFKHDbJ8AzJwO9dnRwtR4s5iaSqZd5CEKQcl4c88JIa7IF8L1PxHbBIIQ6RisesfeGVleeEyJdvQtcFKxjSZIVhdJGwY3xPqY7RPqzTDrOPHfVolOR5OeLu190A7urhiht9MypnzcURRCow62b3KSM0OVPiccnkit4fxZmTUZ9KPEnjqsYKGa9ov9kuLUqNM7wN66qiLV9CNoet8DyBtnk5mbAuYMLx3weM2Sd7WhdESQzMeytxehXTtqSCCJE2UTpTrhSHOrInvtqjwdbm1N2JNlgfe0TN4FnYlNfo8tDhTcsj26FZwvsUV0lMRqAWSYyYrouZvmKpU22I9hMiJypNx0REQpuo82AjXMyTySvkPcBUgOCWOIUP1JqDnXmMbUn56ohOCMtvhULUMEtBhQKfT3b7rbTDzhQsVMXejHGL6ehy9iROf7b4zVBrVXFtxCGnhsw0iTk3kZit4b49cox0qQ5UZcRi83x04qpdZXGUpeN0mLd9Ek96E7cuNEm0815XfROvfTiKUD3PMlV8i6gH16aF9UBEuVb4mJobtZJzns7i0IR4htFkqAHEXa6cTHx252ee5yuAqNSHgo207gxFrKNFFuCpDFkkTdfDUMpHtD8fV5wf9PcIpC9Ivi6WGPWtL96EsEIU2RFyKYNofauyKxSzb8Eecoj3WtZMNaDgMigkwB3h9RnOulA8M1qjCiUtfiuRCXHiPyNkz1Rdpom9FCanovR1mANOfupqnnZrjzZKymu3hs56aDPd6NBZvkmxn3TUgxgCfHKhWQ07mqBhk5ctd3s2ZXKSgEjvEu8GAq26zoFd7W0APorHMPWRE2wV95IrtH5aIkjHebBcb7LfDPLY8YtulAWOPOLz0IakXBHYh2KFcE8OzJellSqEMsmBbCIdV60kBSJZKwTasHWJR4x41eYQRPKM8yyGtjcGFsKWWOIdrgNJnnSxTy0y0iTCl4KzYPm27bjuGbtuVUBxcbWgTGmDsU44pSNdlbIAbjDMJsIZtLRhdIFtk2EsU9Q27SF4nxJOxtCdJoicsGxty4bvgorIfsZ3fMmyly2ARbRLoDWhaPjEXsXah8hi00uVnIAjCqSsIWPMxhSdaNDBlUZUXg8P1FsshNwAJalKvX8pESJF4nFSi90YCcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorH5dD0zsp99nwoCu3uJe8oBUsREVvbX8IQazsznMpcIuBA5c2Js4EyUHNCLS2riu4Itcwy8EfTD2v4BNvkgrIyIASykkKkQOcOVVREq7WMK9CQhm5R5fSIaO9xU4GW113Ikdesi5Q0drxSdhpN98eNa1wWmeR5QgfCuFUWEvFHkXVN4IvujhV0ydHSwWPGbG5Qn6fPTSoWwF2pQjSzO9y6iw3fsKVG8qNWdsmKAPf0HN3RXoYfPgIK1fzEiHU1giFOCNNV1dZwYdRxtGX2U5Iffxsc3nxB1qGFgs8vjzY8qgYNTwoUiTpZBF47X5u4Xeluf04m7fQZbV4mR6WH3W9PdRnWUEbCQer3GWafdeQgcB5mfbsD4ITx2ucDtispHsJ4Gn40huwnTk14yl2Ml8XLhuLtLSbtywaU7toAhRwMshFA5NUSJqJh8YKgCXTFperjdTx5OETv8QlcAeVAI4UUJwHrAWwbc34N1Li8GKaSShxmmAygZKnED9QrADtFjHViEO6NckbWBrOTKmrsQhJRgMcsrDkuMwUo0yqGw13mM1sSrGAcbtjI1LL7wYYsNJgCecYdtkm3SG56syXan6Yp6vHO3HWa7XXCKmpXNwGfrEZXGdCPocH4cCaNQDrSS8K7bbGYVOoDPGNzBfifWztnXJFXMWxwGBaYyRbmQZzq0JBeVvXvzBPY3UXU3laQ49mqh4rUzZaP5a0WsXQkkCUAF1Y3wdnwGti25SavV1jNywLLppHitPEVt7nX4V2hPDrE8a3y05vHQPAt6LPzmfEExr4xRVX2IPfUj5Ojtjy4BGiiz5EzxuiUCTdyuSNcRlgmQkNxHA7LwbOexLTKBuOBW3zoGQxSoz1CdxSzw1a3MxPG3o40K48rAAunQiBVnnjJVbC1YO8fvzwhjN8GMfKgoqG1eJsKN6GfXuZ9QbHMOhbFcQyYls4jVVmJbjH1oIe9vaKzVHLGHf7IXilDPW9qpWo5MJrHdJxBTPXlYogR2wyHunYdlu4dOMzf5c0UCGCxKiMfheF4TZAj21PzISx7xezC25qVtYMMnexNTyUIYVR2n0vSvx6t46qmB8zlF9gb3VpZPTNtD8gep0sBtKHSlouFRyMDhfGYSKFcVmbTsUXHUuru9wIhIfXJLlt2Zist92ThgScU4xKuhYQRvygCI8APCAKjtJWOhOlUBLHD9042LZK1soqEpeYDxSQfd9X9euyDRypH7hAmMIMcFljq7mCtZZxWFS01BiDiPNM55LK4Ua1CCQnG9SbnFjGRZh9iiMYbUsN7OlrTnoMrDmS1WrHL7TVpLDuFBkKXPrgFmvZPhO0z9kN8jYAxLTidsNsYglmKjaZcVkx2SG5lACSAlxTfBMrMOM3dqLqd563wvKSuYjmuHaq14sMSv2yLlXgHkKhW8HQQZmmoTXGuATYlmiH9c9I4moSF8uptGeL9lhxtg2Xe4UlaIGezqEtHfMEc9yvfOz23oRRJc39tN1CZ5bHy5MYSYb27q1YwJPUsGV6RxTS0Hbk5nWubuDnzy9iIbNnTNce9yzJymSCMtEn9JhQszEgIbqfl6Zj7c7OJ55vttLho3TNfjx7CoaZjoOgGQSWoyAvKA1QahqxwomAPBoKhh9coBCWTjhlYSnYrbSZmyfGUGcCCLzZ6FlnCubugY86T7lisiQzJUfQHFRwnCJyxGL7zHG9GTCNw3d5qcYCLkhZuQTRAHkV92qNmeaBzbA5If4Z6RjUYih8NxkbxPcV5V7Tz8iBE2LWK4oRSTVtgDesj77hcIA5hKCcYUq32pb7ZzeBWCy8MDPgSjTIknK3pHX30yZFFYLd9yxvxWBfOvG5q8tkrhJ8tVfQ1os4UlEzc6ZQlK1MUxyJ5WMtlhb8eITssCFr6YyqUpOjGzQzYXb66EsPQ7mjpTmDWRVbVDUP64cNr5UJKfYA9aDy6qze9r1qCqAxhWZ1BRqj0CApjvsVWhxLtED7fKtcwAf7P5vIkAQhcHznI25LS1etkD1Z31RRHYxPKcnV9FH7n4M2tN41L002Q2Os5t1VGe1vdYvJDZJ6wtdHTTTV1QLh9XESLBSJbgc1jdjfpKGoICmpXwayhnrvkCpwK8Ky8rIwWP3qAFoy8JYtFb6ht2bZfaDR6asPIp6caWzjxBmKDsHr5t6yOolhgWkOT6gLwaTua15vSYfTrsJ4beh1anr6HPqkEV0lvPcgwuL67T9QMJmWC3WV6ybUvd9Hh5j7ZzGjN2KSiJe9CtokdAL6gJUa9mi0DE9HuSijkGnOfRbkOhHZH7eTiFfehfdSmpp1HVgPmJxRD1C03j6ZMkabD1GZlCvMHqyLdQAnQtZ6LRYj9MarzXh1s3lTcA5jkCkIme9mOq7HAYpOp4qvVsMdMAjtZ6DpGdCWTbETVHrYNOlZQewQMXOH3hRWHBoslKqZ0tRazwu1L9O854Z1323qdntAKIZiDO121jGT5HfQekOWyHmgTG7BepNTnXzIQN3OAyXM5EOIQEFIpE6lhBAh6gkPwLX9Y8S7hzoHHdOjo4b0ib8ER3Eer5d8j61I1RB9ysww0rkaBy1xEyiG46yvX986l5fVLbpmideIyOZMcbR8ixi78QXMKpHvQlUrwok3VTs0KHxPWJipsm5krKnicZZ69htL4FBCgADbMQB8FKYQoOvTYOdoUAMInImKVZpQCtR9SXBoy92aPWGJVuPkXrDLPIYRzWcmNyC7SchyBDIhq9HUZaSEfiOyUDsVqettJIq1NRjkn2IACxHaV0U4LnKHnrYzTnnLBNyCDmxm6GVhjlZJLFcGVE0N6maykj9aO5o1Uj6ea7cOoCvqqTV1vRXrMumCrgOEmVRBQRtpPqMrE7IrUDtOQb05EveDoXs6Ojw0ZspozNbwJ6dV8IgyiPYBHyeHxjCaBYDyAvpwJe0GKOuahucjVXrPODURxFiEDYJ30Y8NVP9E0kRxNi5hAUKkKOsNP5n4wBZrr73FDk7zlYvbJzLaBOuSCoKXvaAAj7oTzutLlOykNThXJMXeVtzb1OJ8Ym5j5AIHkZ2IPMy2KoSPmHgLNIe8OlPAvv8DoeJdzMQRMEaa69JIPFI0dgRdTWMME3oO3oTgLxo9MWkJE8KD3ZKZ6LYW8QfLbO8hyL8tzEQYu325lrEk6pSGCHEhkfa3vITbElj47YkVNVN4tPxjao6p8cz7lU9cM4Q6UTi18yIJ9lvrzXYHQ9TgaOymrsKkHP5KdqS87XhakYbi8VtFOty2inQglcfZ9odvBaElvcjm7Ymb9ZcFjTTlXs3d8FsgAwidd6ePO569qTPRB5AGgavaTuybMAlXU0D7revU1LpuryY6EABShmAFfum9jHL4xyVA7xOaJwUGaBXUNhAwGoiYXUv8zmw1lkb3m9NNLxqMG47nG4fjlGDnMRX6l6VaGyEwaJWVafFfkksBYVZbE8THLgFSlOOSQ4G6gLzykywaVxxA8KqsaenSdnpkJnhtS3WLGjEJbKkmurG5aJjA0J9PIcUsqETOAEMdWuyl5PB92pQQ97w01w3IJlJx36eIJt4yTSty0gcQ8esXVT8aiEKgpdaoTnThO9sTt2lfm1wpaviqATmCKG7gn3evJFD922QiLEybe1jlHdjCcJ36wwafv729CpegsDPWPWlT92pm8LBmIuCsdjHN1Ks2nn6Duvl4WEZDBb5ksgO0WXYBGng0rouzQfY0T29d17V7ElhBFrcRyZiru7LPPyenrRWCbZCeIqQgtp6AwfsckS5dBPwCiC2vtdaliwWdqigekANGBR4g8cMEMue1aL6N2AVSY7TfSFLsy8NERI0TxvVmrLzvqetaKSPs8FHwlbNRJDzGTplM8iy3vNQyijO2Z9ucaLQbsTjov1TdahDnK4ZvD8PIIJfczsTeI1azwVDHultL08JIbV9nte5YoY0CET31TIqbHZDwRN7E9Ozw9JXfqZDm4p1H2ReWdWyKozt2toeoNf61ccaR0MGhRjG1iZPTYtJxV5gR29aGWQk4FCiAv4twRZeCebps1KaCF0lbp3eVdCM6zKxARlkXdRA3cwwA5SPZ9JrlDhVpUIRwwchdlhxH0B8j72WKtLpFKHboZNs4OHtuyKE1gUo89sS6ApCTZLZkddhdeyq452o3d83aRAqz3aVndL6cuFoyupJe6LVZ9l2UqbxuMW8VZ6flu6MYXpq2b1NwJQjPlQhvYhMltxGKNiDmMoc9BH7akSIO4MA8aGwYzdV7OFdE4xt4LIySWLHrM9nfLYOlZOx80XdNwZtxmNIGVd4cVRzbq0NHjFjm6FdTBEG0zvWXrdDwq5ErpJFsAEcHw8Va7JNMZFbbZaiDd1pdAgtX1nEMWqDquehFy7VyWZW4HdvrZpJa4FaVFHcVGLJlO2boCoqebdUQhVc7vUvaO124p4gGNHo2RsjfwSgC0cwmg0vnVMiex1ezcBcbfc1kXwKixauyMWFKDOvomcolbaSA3OlTScmTfXS2LJixdLOXOBeSD28Ym2klVq6IS5CIGKwpn0CcciycI35ReYMK5f8r1JG3iYnR1jpeCASt9ggmT87eP0zgu01YYVUMNRgm9mDYU16Pce36HwDoD6yNxp6Czk3kJUpqygSpntVyuf6uuOsFwUfi3ii9TZqMlsbbcEqlYybVh5K5OjcZBCzmYqF4D1aLWfDtRDuc314paLfOga7J2ghuujoTXeqP328GdqQmCfsQEvUw9Oxef7hA7X0Bx68XCJ2yMKEaNOdirG5ubXxVQPFb7YFTdtNrMqPpbpVpqMRqU506LGTRnKYD90qqPF7ctgbIfjzQLV5CDymsiclAg77rLgw8T7p9o9CXIJesrZR95YZKGgsE7FOlpnsOrVSnjwQFO6u1I5oe1Q2uoQ33BfrzxwQ99pkZilFlZOzs6xsMtCb6h8E71TtfrLbwoPv15WniTNwvpYIq807Aq8zzVchw5xFF2cX1KohDIDnef8c0EztchHVvwTEOmpOrqslhl5n5yAa1vgorHThisisAnAdditionalBitofTextAddedSoWeCanMakeSureOurShortLimitIsFixedForMoreThanOneStringLongerThanItsLimit + \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-fr/strings.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-fr/strings.xml new file mode 100644 index 00000000..ad496d77 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-fr/strings.xml @@ -0,0 +1,5 @@ + + + http://www.foo.com + http://www.foo.com + \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/anims.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/anims.xml new file mode 100644 index 00000000..cdd947c9 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/anims.xml @@ -0,0 +1,12 @@ + + + @android:anim/bounce_interpolator + @android:anim/cycle_interpolator + @android:anim/decelerate_interpolator + @android:anim/fade_in + @android:anim/fade_out + @android:anim/linear_interpolator + @android:anim/overshoot_interpolator + @android:anim/slide_in_left + @android:anim/slide_out_right + diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/arrays.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/arrays.xml index 8cc3d152..52c087c8 100644 --- a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/arrays.xml +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/arrays.xml @@ -5,6 +5,9 @@ TEST2 TEST3 %2$s foo %1$d + http://google.com&boo=1&foo=2 + <b>Bolded Text</b> + <font size=16 align=middle>Small</font> -1 diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/colors.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/colors.xml index 00dfdb0f..038b11e3 100644 --- a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/colors.xml +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/colors.xml @@ -1,4 +1,6 @@ #ff123456 + @android:color/white + #00000000 diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/drawables.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/drawables.xml new file mode 100644 index 00000000..5f970b28 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/drawables.xml @@ -0,0 +1,5 @@ + + + @android:drawable/btn_default + #00000000 + diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/layouts.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/layouts.xml new file mode 100644 index 00000000..391ab37b --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/layouts.xml @@ -0,0 +1,7 @@ + + + @android:layout/activity_list_item + @android:layout/browser_link_context_header + @android:layout/simple_list_item_1 + @android:layout/simple_spinner_item + diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/plurals.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/plurals.xml new file mode 100644 index 00000000..685c7623 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/plurals.xml @@ -0,0 +1,19 @@ + + + + %1$s, %2$d foo(s) + %1$s, %2$d foo + + + %d foos + %d foo + + + foo %d moos + foo %d moo + + + foo %d + foo 1 + + \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml index 6beed51c..e9cd78fb 100644 --- a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/strings.xml @@ -17,11 +17,15 @@ bar" foobar"b az"foo %d of %d - foo %d bar % + foo %d bar % %2$s foo %1$d %-e foo %,d %2$-e foo %1$,d %02d foo %01d %d foo %1 %1% foo %2% + foo %s bar %2$s baz + 賞金鬥士14 + {id:65538,v:2,tid:20003,mst:1,x:-1,y:-1,a:6000,b:3000,lm:{chp:1000,rep:0,bt:0,mp:[[101,0,1,0],[101,0,1,1],[101,0,1,2],[101,0,1,3],[101,0,1,4],[101,0,1,5],[100,0,0,0],[100,0,0,1],[100,0,0,2],[100,0,0,3],[100,0,0,4],[100,0,0,5]]},rm:{chp:1000,rep:0,bt:0,mp:[[100,0,1,0],[100,0,1,1],[100,0,1,2],[100,0,1,3],[100,0,1,4],[100,0,1,5],[101,0,0,0],[101,0,0,1],[101,0,0,2],[101,0,0,3],[101,0,0,4],[101,0,0,5]]}} + {al:[[180,0,7,0,0,1000],[109,0,5,0,0],[109,0,5,2,0],[109,0,5,4,0],[100,0,3,0,0],[100,0,3,1,0],[100,0,3,2,0],[100,0,3,3,0],[100,0,3,4,0],[100,0,3,5,0],[103,0,1,0,0],[103,0,1,1,0],[103,0,1,2,0],[103,0,1,3,0],[103,0,1,4,0],[103,0,1,5,0],[106,0,2,0,0],[106,0,2,1,0],[106,0,2,2,0],[106,0,2,3,0],[106,0,2,4,0],[106,0,2,5,0],[800,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[850],[950],[900,0],[1000,0,0]],v:4,s:-1575044211,rl:[[180,0,7,0,0,1000],[103,0,0,0,0],[103,0,0,1,0],[103,0,0,2,0],[103,0,0,3,0],[103,0,0,4,0],[103,0,0,5,0],[111,0,3,0,0],[111,0,3,1,0],[111,0,3,2,0],[111,0,3,3,0],[111,0,3,4,0],[111,0,3,5,0],[102,0,4,0,0],[102,0,4,1,0],[102,0,4,2,0],[102,0,4,3,0],[102,0,4,4,0],[102,0,4,5,0],[107,0,5,0,0],[107,0,5,1,0],[107,0,5,2,0],[107,0,5,3,0],[107,0,5,4,0],[107,0,5,5,0],[106,0,2,0,0],[106,0,2,1,0],[106,0,2,2,0],[106,0,2,3,0],[106,0,2,4,0],[106,0,2,5,0],[900],[1000,0,0]],m:[]} diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/styles.xml b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/styles.xml new file mode 100644 index 00000000..29649cff --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/testapp/res/values-mcc001/styles.xml @@ -0,0 +1,11 @@ + + + + +