diff options
author | Alex Light <allight@google.com> | 2015-09-28 16:43:03 -0700 |
---|---|---|
committer | Alex Light <allight@google.com> | 2015-09-28 16:43:03 -0700 |
commit | bdccf9c3ef6f14aa499e1e1f31ed0742cbdaa2a5 (patch) | |
tree | 4384941ba7ed66a92cbb80c18960d19a93182ae5 | |
parent | db3791542b8e836b60544b8941a17628c9aad1f9 (diff) | |
parent | 613c493e9698812c0531acf073bc7ca9e4538eac (diff) | |
download | smali-bdccf9c3ef6f14aa499e1e1f31ed0742cbdaa2a5.tar.gz |
Merge remote-tracking branch 'aosp/upstream-master' into aosp/master
* Brings up c456c55c40d0731edb9913fae73f16b9d94ac45b which fixes a
smali bug relating to interface ordering.
26 files changed, 323 insertions, 101 deletions
@@ -2,16 +2,16 @@ smali/baksmali is an assembler/disassembler for the dex format used by dalvik, Android's Java VM implementation. The syntax is loosely based on Jasmin's/dedexer's syntax, and supports the full functionality of the dex format (annotations, debug info, line info, etc.) -The primary webpage is http://smali.googlecode.com, and the source is also mirrored at https://github.com/jesusfreke/smali. If you are interested in submitting a patch, feel free to send me a pull request on either site. +Downloads are at https://bitbucket.org/JesusFreke/smali/downloads. If you are interested in submitting a patch, feel free to send me a pull request here. #### Support -- [googlecode Issue tracker](https://code.google.com/p/smali/issues/list) - For any bugs/issues/feature requests -- [#smali on freenode](http://webchat.freenode.net/?channels=smali) - Free free to drop by and ask a question. Don't expect an instant response, but if you hang around someone will respond. Think of it more in terms of.. multi-player notepad. +- [github Issue tracker](https://github.com/JesusFreke/smali/issues) - For any bugs/issues/feature requests +- [#smali on freenode](http://webchat.freenode.net/?channels=smali) - Free free to drop by and ask a question. Don't expect an instant response, but if you hang around someone will respond. #### Some useful links for getting started with smali - [Official dex bytecode reference](https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html) -- [Registers wiki page](https://code.google.com/p/smali/wiki/Registers) -- [Types, Methods and Fields wiki page](https://code.google.com/p/smali/wiki/TypesMethodsAndFields) +- [Registers wiki page](https://github.com/JesusFreke/smali/wiki/Registers) +- [Types, Methods and Fields wiki page](https://github.com/JesusFreke/smali/wiki/TypesMethodsAndFields) - [Official dex format reference](https://source.android.com/devices/tech/dalvik/dex-format.html) diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java index 9c171f49..2529af8a 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java @@ -146,8 +146,7 @@ public class ClassDefinition { } private void writeInterfaces(IndentingWriter writer) throws IOException { - List<String> interfaces = Lists.newArrayList(classDef.getInterfaces()); - Collections.sort(interfaces); + List<String> interfaces = classDef.getInterfaces(); if (interfaces.size() != 0) { writer.write('\n'); diff --git a/baksmali/src/test/java/org/jf/baksmali/InterfaceOrderTest.java b/baksmali/src/test/java/org/jf/baksmali/InterfaceOrderTest.java new file mode 100644 index 00000000..d85d7913 --- /dev/null +++ b/baksmali/src/test/java/org/jf/baksmali/InterfaceOrderTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2015, 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 org.junit.Test; + +public class InterfaceOrderTest extends IdenticalRoundtripTest { + @Test + public void testInterfaceOrder() { + runTest("InterfaceOrder", new baksmaliOptions()); + } +} diff --git a/baksmali/src/test/resources/InterfaceOrderTest/InterfaceOrder.smali b/baksmali/src/test/resources/InterfaceOrderTest/InterfaceOrder.smali new file mode 100644 index 00000000..b4745cb3 --- /dev/null +++ b/baksmali/src/test/resources/InterfaceOrderTest/InterfaceOrder.smali @@ -0,0 +1,37 @@ +.class public LInterfaceOrder; +.super Ljava/lang/Object; + +# Note how these two interfaces are not in alphabetical order +.implements Ljava/io/Serializable; +.implements Ljava/util/EventListener; +.implements Ljava/lang/Runnable; +.implements Ljava/io/Flushable; +.implements Ljava/lang/Clonable; +.implements Ljava/util/Observer; +.implements Ljava/io/Closeable; + +# direct methods +.method public constructor <init>()V + .registers 1 + return-void +.end method + +.method public close()V + .registers 1 + return-void +.end method + +.method public flush()V + .registers 1 + return-void +.end method + +.method public run()V + .registers 1 + return-void +.end method + +.method public update(Ljava/util/Observable;Ljava/lang/Object;)V + .registers 3 + return-void +.end method diff --git a/build.gradle b/build.gradle index f0d1e00a..edf156fe 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ apply plugin: 'idea' -version = '2.0.5' +version = '2.0.8' def jarVersion = version @@ -57,6 +57,11 @@ if (!('release' in gradle.startParameter.taskNames)) { // 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' +} else { + if (System.env.JDK6_HOME == null && !JavaVersion.current().isJava6()) { + throw new InvalidUserDataException("bzzzzzzzt. Release builds must be performed with java 6. " + + "Either run gradle with java 6, or define the JDK6_HOME environment variable.") + } } // Note: please don't use this. This is strictly for the official releases @@ -79,6 +84,19 @@ subprojects { } } + if (System.env.JDK6_HOME != null) { + sourceCompatibility = 1.6 + targetCompatibility = 1.6 + + tasks.withType(JavaCompile) { + doFirst { + options.fork = true + options.bootClasspath = "$System.env.JDK6_HOME/jre/lib/rt.jar" + options.bootClasspath += "$File.pathSeparator$System.env.JDK6_HOME/jre/lib/jsse.jar" + } + } + } + version = parent.version ext { @@ -90,7 +108,7 @@ subprojects { stringtemplate: 'org.antlr:stringtemplate:3.2.1', commons_cli: 'commons-cli:commons-cli:1.2', jflex: 'de.jflex:jflex:1.4.3', - jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.1', + jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.2', proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1', dx: 'com.google.android.tools:dx:1.7' ] diff --git a/dexlib2/build.gradle b/dexlib2/build.gradle index dc3e853a..8fbe5ffe 100644 --- a/dexlib2/build.gradle +++ b/dexlib2/build.gradle @@ -29,6 +29,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +ext.testAccessorOutputDir = file("${buildDir}/generated-src/accessorTest/java") +ext.testAccessorOutputFile = file("${testAccessorOutputDir}/org/jf/dexlib2/AccessorTypes.java") + +sourceSets { + accessorTest { + java { + srcDir testAccessorOutputDir + } + } +} + configurations { accessorTestGenerator dx @@ -46,9 +57,6 @@ dependencies { dx depends.dx } -ext.testAccessorOutputDir = file("${buildDir}/generated-accessor-test-sources") -ext.testAccessorOutputFile = file("${buildDir}/generated-accessor-test-sources/org/jf/dexlib2/AccessorTypes.java") - // You must manually execute this task to regenerate SyntheticAccessorFSM.java, after modifying the ragel file // e.g. ./gradlew ragel task ragel(type:Exec) { @@ -59,48 +67,31 @@ task ragel(type:Exec) { } task generateAccessorTestSource(type: JavaExec) { - doFirst { - file(testAccessorOutputFile.parent).mkdirs() - } - + file(testAccessorOutputFile.parent).mkdirs() outputs.dir file(testAccessorOutputDir) - sourceSets['test'].java.srcDir file(testAccessorOutputDir) classpath = configurations.accessorTestGenerator main = 'org.jf.dexlib2.AccessorTestGenerator' args testAccessorOutputFile } -compileTestJava.dependsOn generateAccessorTestSource - -task generateAccessorTestDex(type: JavaExec, dependsOn: compileTestJava) { - def outputDex = file(new File(sourceSets.test.output.resourcesDir, 'accessorTest.dex')) +compileAccessorTestJava.dependsOn(generateAccessorTestSource) - doFirst { - file(outputDex.parent).mkdirs() - - // this has to be done in doFirst, so that the generated classes will be available. - // otherwise, it the tree will be populated while the build is being configured, - // which is before the compileTestJava has run - fileTree(project.sourceSets.test.output.classesDir) { - include 'org/jf/dexlib2/AccessorTypes*.class' - }.each { File file -> - args file - } - } +// You must manually execute this task to regenerate src/test/resources/accessorTest.dex +task generateAccessorTestDex(type: JavaExec, dependsOn: compileAccessorTestJava) { + def outputDex = file('src/test/resources/accessorTest.dex') + file(outputDex.parent).mkdirs() - inputs.dir(project.sourceSets.test.output.classesDir) + inputs.dir(project.sourceSets.accessorTest.output.classesDir) outputs.file outputDex main 'com.android.dx.command.Main' classpath = configurations.dx - workingDir project.sourceSets.test.output.classesDir - //executable 'dx' args '--dex' args '--no-strict' args "--output=${outputDex}" + args sourceSets.accessorTest.output.classesDir } -test.dependsOn generateAccessorTestDex uploadArchives { repositories.mavenDeployer { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java index a1ddee2e..113b60a3 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java @@ -45,12 +45,23 @@ import java.util.zip.ZipFile; public final class DexFileFactory { @Nonnull + public static DexBackedDexFile loadDexFile(String path, int api) + throws IOException { + return loadDexFile(path, api, false); + } + + @Nonnull public static DexBackedDexFile loadDexFile(String path, int api, boolean experimental) throws IOException { return loadDexFile(new File(path), "classes.dex", new Opcodes(api, experimental)); } @Nonnull + public static DexBackedDexFile loadDexFile(File dexFile, int api) throws IOException { + return loadDexFile(dexFile, api, false); + } + + @Nonnull public static DexBackedDexFile loadDexFile(File dexFile, int api, boolean experimental) throws IOException { return loadDexFile(dexFile, "classes.dex", new Opcodes(api, experimental)); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java b/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java index dd876813..e718e275 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java @@ -40,6 +40,10 @@ public class Opcodes { private final Opcode[] opcodesByValue; private final HashMap<String, Opcode> opcodesByName; + public Opcodes(int api) { + this(api, false); + } + public Opcodes(int api, boolean experimental) { opcodesByValue = new Opcode[256]; opcodesByName = Maps.newHashMap(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionClassDef.java index fc1dc62f..7e8a2829 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionClassDef.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/reflection/ReflectionClassDef.java @@ -33,6 +33,7 @@ package org.jf.dexlib2.analysis.reflection; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils; @@ -48,6 +49,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.AbstractSet; import java.util.Iterator; +import java.util.List; import java.util.Set; /** @@ -78,23 +80,17 @@ public class ReflectionClassDef extends BaseTypeReference implements ClassDef { return ReflectionUtils.javaToDexName(superClass.getName()); } - @Nonnull @Override public Set<String> getInterfaces() { - return new AbstractSet<String>() { - @Nonnull @Override public Iterator<String> iterator() { - return Iterators.transform(Iterators.forArray(cls.getInterfaces()), new Function<Class, String>() { - @Nullable @Override public String apply(@Nullable Class input) { - if (input == null) { - return null; - } - return ReflectionUtils.javaToDexName(input.getName()); - } - }); + @Nonnull @Override public List<String> getInterfaces() { + return ImmutableList.copyOf(Iterators.transform(Iterators.forArray(cls.getInterfaces()), new Function<Class, String>() { + @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() { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java index 9596a278..88f1dce9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java @@ -31,6 +31,7 @@ package org.jf.dexlib2.dexbacked; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import org.jf.dexlib2.base.reference.BaseTypeReference; @@ -47,7 +48,9 @@ import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.AbstractList; import java.util.Iterator; +import java.util.List; import java.util.Set; public class DexBackedClassDef extends BaseTypeReference implements ClassDef { @@ -114,21 +117,21 @@ public class DexBackedClassDef extends BaseTypeReference implements ClassDef { @Nonnull @Override - public Set<String> getInterfaces() { + public List<String> getInterfaces() { final int interfacesOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.INTERFACES_OFFSET); if (interfacesOffset > 0) { final int size = dexFile.readSmallUint(interfacesOffset); - return new FixedSizeSet<String>() { - @Nonnull + return new AbstractList<String>() { @Override - public String readItem(int index) { + @Nonnull + public String get(int index) { return dexFile.getType(dexFile.readUshort(interfacesOffset + 4 + (2*index))); } @Override public int size() { return size; } }; } - return ImmutableSet.of(); + return ImmutableList.of(); } @Nonnull diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java index f26b7e12..eccb9218 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java @@ -32,7 +32,7 @@ package org.jf.dexlib2.dexbacked; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterators; +import com.google.common.collect.ImmutableSet; import org.jf.dexlib2.base.reference.BaseMethodReference; import org.jf.dexlib2.dexbacked.raw.MethodIdItem; import org.jf.dexlib2.dexbacked.raw.ProtoIdItem; @@ -152,7 +152,7 @@ public class DexBackedMethod extends BaseMethodReference implements Method { if (methodImpl != null) { return methodImpl.getParameterNames(null); } - return Iterators.emptyIterator(); + return ImmutableSet.<String>of().iterator(); } @Nonnull diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java index 0c06b1d7..676d86cd 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java @@ -123,7 +123,16 @@ public class DexBackedMethodImplementation implements MethodImplementation { @Nonnull private DebugInfo getDebugInfo() { - return DebugInfo.newOrEmpty(dexFile, dexFile.readSmallUint(codeOffset + CodeItem.DEBUG_INFO_OFFSET), this); + int debugOffset = dexFile.readInt(codeOffset + CodeItem.DEBUG_INFO_OFFSET); + + if (debugOffset == -1 || debugOffset == 0) { + return DebugInfo.newOrEmpty(dexFile, 0, this); + } + if (debugOffset < 0) { + System.err.println("%s: Invalid debug offset"); + return DebugInfo.newOrEmpty(dexFile, 0, this); + } + return DebugInfo.newOrEmpty(dexFile, debugOffset, this); } @Nonnull @Override diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java index 9c79e270..27d72ad1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/CodeItem.java @@ -100,10 +100,10 @@ public class CodeItem { int triesCount = reader.readUshort(); out.annotate(2, "tries_size = %d", triesCount); - int debugInfoOffset = reader.readSmallUint(); + int debugInfoOffset = reader.readInt(); out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset); - if (debugInfoOffset != 0) { + if (debugInfoOffset > 0) { addDebugInfoIdentity(debugInfoOffset, itemIdentity); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java index 6759820e..8a32b5f6 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java @@ -31,12 +31,12 @@ package org.jf.dexlib2.dexbacked.util; -import com.google.common.collect.Iterators; +import com.google.common.collect.ImmutableSet; import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.DebugItemType; +import org.jf.dexlib2.dexbacked.DexBackedDexFile; import org.jf.dexlib2.dexbacked.DexBackedMethod; import org.jf.dexlib2.dexbacked.DexBackedMethodImplementation; -import org.jf.dexlib2.dexbacked.DexBackedDexFile; import org.jf.dexlib2.dexbacked.DexReader; import org.jf.dexlib2.iface.MethodParameter; import org.jf.dexlib2.iface.debug.DebugItem; @@ -70,9 +70,13 @@ public abstract class DebugInfo implements Iterable<DebugItem> { private static class EmptyDebugInfo extends DebugInfo { public static final EmptyDebugInfo INSTANCE = new EmptyDebugInfo(); private EmptyDebugInfo() {} - @Nonnull @Override public Iterator<DebugItem> iterator() { return Iterators.emptyIterator(); } + + @Nonnull @Override public Iterator<DebugItem> iterator() { + return ImmutableSet.<DebugItem>of().iterator(); + } + @Nonnull @Override public Iterator<String> getParameterNames(@Nullable DexReader reader) { - return Iterators.emptyIterator(); + return ImmutableSet.<String>of().iterator(); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/ClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/ClassDef.java index 31d7fb99..78e17b15 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/ClassDef.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/ClassDef.java @@ -35,6 +35,7 @@ import org.jf.dexlib2.iface.reference.TypeReference; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.List; import java.util.Set; /** @@ -72,11 +73,11 @@ public interface ClassDef extends TypeReference, Annotatable { @Nullable String getSuperclass(); /** - * Gets a set of the interfaces that this class implements. + * Gets a list of the interfaces that this class implements. * - * @return A set of the interfaces that this class implements + * @return A list of the interfaces that this class implements */ - @Nonnull Set<String> getInterfaces(); + @Nonnull List<String> getInterfaces(); /** * Gets the name of the primary source file that this class is defined in, if available. diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableClassDef.java index 42e14549..8bdfff26 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableClassDef.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableClassDef.java @@ -47,12 +47,13 @@ import javax.annotation.Nullable; import java.util.AbstractCollection; import java.util.Collection; import java.util.Iterator; +import java.util.List; public class ImmutableClassDef extends BaseTypeReference implements ClassDef { @Nonnull protected final String type; protected final int accessFlags; @Nullable protected final String superclass; - @Nonnull protected final ImmutableSet<String> interfaces; + @Nonnull protected final ImmutableList<String> interfaces; @Nullable protected final String sourceFile; @Nonnull protected final ImmutableSet<? extends ImmutableAnnotation> annotations; @Nonnull protected final ImmutableSortedSet<? extends ImmutableField> staticFields; @@ -78,7 +79,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef { this.type = type; this.accessFlags = accessFlags; this.superclass = superclass; - this.interfaces = interfaces==null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(interfaces); + this.interfaces = interfaces==null ? ImmutableList.<String>of() : ImmutableList.copyOf(interfaces); this.sourceFile = sourceFile; this.annotations = ImmutableAnnotation.immutableSetOf(annotations); this.staticFields = ImmutableField.immutableSetOf(Iterables.filter(fields, FieldUtil.FIELD_IS_STATIC)); @@ -100,7 +101,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef { this.type = type; this.accessFlags = accessFlags; this.superclass = superclass; - this.interfaces = interfaces==null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(interfaces); + this.interfaces = interfaces==null ? ImmutableList.<String>of() : ImmutableList.copyOf(interfaces); this.sourceFile = sourceFile; this.annotations = ImmutableAnnotation.immutableSetOf(annotations); this.staticFields = ImmutableField.immutableSetOf(staticFields); @@ -112,7 +113,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef { public ImmutableClassDef(@Nonnull String type, int accessFlags, @Nullable String superclass, - @Nullable ImmutableSet<String> interfaces, + @Nullable ImmutableList<String> interfaces, @Nullable String sourceFile, @Nullable ImmutableSet<? extends ImmutableAnnotation> annotations, @Nullable ImmutableSortedSet<? extends ImmutableField> staticFields, @@ -122,7 +123,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef { this.type = type; this.accessFlags = accessFlags; this.superclass = superclass; - this.interfaces = ImmutableUtils.nullToEmptySet(interfaces); + this.interfaces = ImmutableUtils.nullToEmptyList(interfaces); this.sourceFile = sourceFile; this.annotations = ImmutableUtils.nullToEmptySet(annotations); this.staticFields = ImmutableUtils.nullToEmptySortedSet(staticFields); @@ -151,7 +152,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef { @Nonnull @Override public String getType() { return type; } @Override public int getAccessFlags() { return accessFlags; } @Nullable @Override public String getSuperclass() { return superclass; } - @Nonnull @Override public ImmutableSet<String> getInterfaces() { return interfaces; } + @Nonnull @Override public ImmutableList<String> getInterfaces() { return interfaces; } @Nullable @Override public String getSourceFile() { return sourceFile; } @Nonnull @Override public ImmutableSet<? extends ImmutableAnnotation> getAnnotations() { return annotations; } @Nonnull @Override public ImmutableSet<? extends ImmutableField> getStaticFields() { return staticFields; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/rewriter/ClassDefRewriter.java b/dexlib2/src/main/java/org/jf/dexlib2/rewriter/ClassDefRewriter.java index ad246e5b..7e34ee33 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/rewriter/ClassDefRewriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/rewriter/ClassDefRewriter.java @@ -41,6 +41,7 @@ import org.jf.dexlib2.iface.Method; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Iterator; +import java.util.List; import java.util.Set; public class ClassDefRewriter implements Rewriter<ClassDef> { @@ -73,8 +74,8 @@ public class ClassDefRewriter implements Rewriter<ClassDef> { return RewriterUtils.rewriteNullable(rewriters.getTypeRewriter(), classDef.getSuperclass()); } - @Override @Nonnull public Set<String> getInterfaces() { - return RewriterUtils.rewriteSet(rewriters.getTypeRewriter(), classDef.getInterfaces()); + @Override @Nonnull public List<String> getInterfaces() { + return RewriterUtils.rewriteList(rewriters.getTypeRewriter(), classDef.getInterfaces()); } @Override @Nullable public String getSourceFile() { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassSection.java index bb2e4a72..d28dd444 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassSection.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/ClassSection.java @@ -53,7 +53,7 @@ public interface ClassSection<StringKey extends CharSequence, TypeKey extends Ch @Nonnull TypeKey getType(@Nonnull ClassKey key); int getAccessFlags(@Nonnull ClassKey key); @Nullable TypeKey getSuperclass(@Nonnull ClassKey key); - @Nullable TypeListKey getSortedInterfaces(@Nonnull ClassKey key); + @Nullable TypeListKey getInterfaces(@Nonnull ClassKey key); @Nullable StringKey getSourceFile(@Nonnull ClassKey key); @Nullable Collection<? extends EncodedValue> getStaticInitializers(@Nonnull ClassKey key); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java index 0650ab3c..be23978e 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java @@ -58,6 +58,7 @@ import org.jf.dexlib2.iface.reference.StringReference; import org.jf.dexlib2.iface.reference.TypeReference; import org.jf.dexlib2.util.InstructionUtil; import org.jf.dexlib2.util.MethodUtil; +import org.jf.dexlib2.util.ReferenceUtil; import org.jf.dexlib2.writer.io.DeferredOutputStream; import org.jf.dexlib2.writer.io.DeferredOutputStreamFactory; import org.jf.dexlib2.writer.io.DexDataStore; @@ -196,6 +197,33 @@ public abstract class DexWriter< classSection.getItems().size() * ClassDefItem.ITEM_SIZE; } + @Nonnull + public List<String> getMethodReferences() { + List<String> methodReferences = Lists.newArrayList(); + for (Entry<? extends MethodRefKey, Integer> methodReference: methodSection.getItems()) { + methodReferences.add(ReferenceUtil.getMethodDescriptor(methodReference.getKey())); + } + return methodReferences; + } + + @Nonnull + public List<String> getFieldReferences() { + List<String> fieldReferences = Lists.newArrayList(); + for (Entry<? extends FieldRefKey, Integer> fieldReference: fieldSection.getItems()) { + fieldReferences.add(ReferenceUtil.getFieldDescriptor(fieldReference.getKey())); + } + return fieldReferences; + } + + @Nonnull + public List<String> getTypeReferences() { + List<String> classReferences = Lists.newArrayList(); + for (Entry<? extends TypeKey, Integer> typeReference: typeSection.getItems()) { + classReferences.add(typeReference.getKey().toString()); + } + return classReferences; + } + public void writeTo(@Nonnull DexDataStore dest) throws IOException { this.writeTo(dest, MemoryDeferredOutputStream.getFactory()); } @@ -405,7 +433,7 @@ public abstract class DexWriter< nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, superEntry); // then, try to write interfaces - for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getSortedInterfaces(key))) { + for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getInterfaces(key))) { Map.Entry<? extends ClassKey, Integer> interfaceEntry = classSection.getClassEntryByType(interfaceTypeKey); nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, interfaceEntry); } @@ -418,7 +446,7 @@ public abstract class DexWriter< indexWriter.writeInt(typeSection.getItemIndex(classSection.getType(key))); indexWriter.writeInt(classSection.getAccessFlags(key)); indexWriter.writeInt(typeSection.getNullableItemIndex(classSection.getSuperclass(key))); - indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getSortedInterfaces(key))); + indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getInterfaces(key))); indexWriter.writeInt(stringSection.getNullableItemIndex(classSection.getSourceFile(key))); indexWriter.writeInt(classSection.getAnnotationDirectoryOffset(key)); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassDef.java index f19a2e7f..10215925 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassDef.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassDef.java @@ -31,6 +31,7 @@ package org.jf.dexlib2.writer.builder; +import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.*; import org.jf.dexlib2.base.reference.BaseTypeReference; @@ -101,16 +102,8 @@ public class BuilderClassDef extends BaseTypeReference implements ClassDef { @Nonnull @Override public SortedSet<BuilderMethod> getVirtualMethods() { return virtualMethods; } @Nonnull @Override - public Set<String> getInterfaces() { - return new AbstractSet<String>() { - @Nonnull @Override public Iterator<String> iterator() { - return Iterators.transform(interfaces.iterator(), Functions.toStringFunction()); - } - - @Override public int size() { - return interfaces.size(); - } - }; + public List<String> getInterfaces() { + return Lists.transform(this.interfaces, Functions.toStringFunction()); } @Nonnull @Override public Collection<BuilderField> getFields() { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java index 8ab53db8..29980f32 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java @@ -122,7 +122,7 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu return builderClassDef.superclass; } - @Nullable @Override public BuilderTypeList getSortedInterfaces(@Nonnull BuilderClassDef builderClassDef) { + @Nullable @Override public BuilderTypeList getInterfaces(@Nonnull BuilderClassDef builderClassDef) { return builderClassDef.interfaces; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java index 469a3324..9a727b27 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java @@ -35,6 +35,7 @@ import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import org.jf.dexlib2.ValueType; import org.jf.dexlib2.iface.Annotation; import org.jf.dexlib2.iface.MethodImplementation; @@ -117,16 +118,15 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR if (interfaces == null) { interfaces = ImmutableList.of(); } else { - interfaces = Lists.newArrayList(interfaces); - Collections.sort(interfaces); - String prev = null; + Set<String> interfaces_copy = Sets.newHashSet(interfaces); Iterator<String> interfaceIterator = interfaces.iterator(); while (interfaceIterator.hasNext()) { String iface = interfaceIterator.next(); - if (prev != null && iface.equals(prev)) { + if (!interfaces_copy.contains(iface)) { interfaceIterator.remove(); + } else { + interfaces_copy.remove(iface); } - prev = iface; } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java index 96eca2ae..0389973a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java @@ -250,7 +250,7 @@ public class ClassPool implements ClassSection<CharSequence, CharSequence, return classDef.getSuperclass(); } - @Nullable @Override public TypeListPool.Key<SortedSet<String>> getSortedInterfaces(@Nonnull PoolClassDef classDef) { + @Nullable @Override public TypeListPool.Key<List<String>> getInterfaces(@Nonnull PoolClassDef classDef) { return classDef.interfaces; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolClassDef.java index fe897d26..00958fb8 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolClassDef.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/PoolClassDef.java @@ -43,7 +43,7 @@ import java.util.*; class PoolClassDef extends BaseTypeReference implements ClassDef { @Nonnull final ClassDef classDef; - @Nonnull final TypeListPool.Key<SortedSet<String>> interfaces; + @Nonnull final TypeListPool.Key<List<String>> interfaces; @Nonnull final ImmutableSortedSet<Field> staticFields; @Nonnull final ImmutableSortedSet<Field> instanceFields; @Nonnull final ImmutableSortedSet<PoolMethod> directMethods; @@ -56,7 +56,7 @@ class PoolClassDef extends BaseTypeReference implements ClassDef { PoolClassDef(@Nonnull ClassDef classDef) { this.classDef = classDef; - interfaces = new TypeListPool.Key<SortedSet<String>>(ImmutableSortedSet.copyOf(classDef.getInterfaces())); + interfaces = new TypeListPool.Key<List<String>>(ImmutableList.copyOf(classDef.getInterfaces())); staticFields = ImmutableSortedSet.copyOf(classDef.getStaticFields()); instanceFields = ImmutableSortedSet.copyOf(classDef.getInstanceFields()); directMethods = ImmutableSortedSet.copyOf( @@ -77,7 +77,7 @@ class PoolClassDef extends BaseTypeReference implements ClassDef { return classDef.getSuperclass(); } - @Nonnull @Override public SortedSet<String> getInterfaces() { + @Nonnull @Override public List<String> getInterfaces() { return interfaces.types; } diff --git a/dexlib2/src/test/resources/accessorTest.dex b/dexlib2/src/test/resources/accessorTest.dex Binary files differnew file mode 100644 index 00000000..456f85f4 --- /dev/null +++ b/dexlib2/src/test/resources/accessorTest.dex diff --git a/smali/src/main/java/org/jf/smali/main.java b/smali/src/main/java/org/jf/smali/main.java index 31f65a88..98fb7a1f 100644 --- a/smali/src/main/java/org/jf/smali/main.java +++ b/smali/src/main/java/org/jf/smali/main.java @@ -28,7 +28,9 @@ package org.jf.smali; +import com.google.common.base.Strings; import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.Token; import org.antlr.runtime.TokenSource; @@ -113,6 +115,15 @@ public class main { boolean printTokens = false; boolean experimental = false; + boolean listMethods = false; + String methodListFilename = null; + + boolean listFields = false; + String fieldListFilename = null; + + boolean listTypes = false; + String typeListFilename = null; + int apiLevel = 15; String outputDexFile = "out.dex"; @@ -153,6 +164,18 @@ public class main { case 'j': jobs = Integer.parseInt(commandLine.getOptionValue("j")); break; + case 'm': + listMethods = true; + methodListFilename = commandLine.getOptionValue("m"); + break; + case 'f': + listFields = true; + fieldListFilename = commandLine.getOptionValue("f"); + break; + case 't': + listTypes = true; + typeListFilename = commandLine.getOptionValue("t"); + break; case 'V': verboseErrors = true; break; @@ -232,6 +255,27 @@ public class main { System.exit(1); } + if (listMethods) { + if (Strings.isNullOrEmpty(methodListFilename)) { + methodListFilename = outputDexFile + ".methods"; + } + writeReferences(dexBuilder.getMethodReferences(), methodListFilename); + } + + if (listFields) { + if (Strings.isNullOrEmpty(fieldListFilename)) { + fieldListFilename = outputDexFile + ".fields"; + } + writeReferences(dexBuilder.getFieldReferences(), fieldListFilename); + } + + if (listTypes) { + if (Strings.isNullOrEmpty(typeListFilename)) { + typeListFilename = outputDexFile + ".types"; + } + writeReferences(dexBuilder.getTypeReferences(), typeListFilename); + } + dexBuilder.writeTo(new FileDataStore(new File(outputDexFile))); } catch (RuntimeException ex) { System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:"); @@ -244,6 +288,23 @@ public class main { } } + private static void writeReferences(List<String> references, String filename) { + PrintWriter writer = null; + try { + writer = new PrintWriter(new BufferedWriter(new FileWriter(filename))); + + for (String reference: Ordering.natural().sortedCopy(references)) { + writer.println(reference); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } finally { + if (writer != null) { + writer.close(); + } + } + } + private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) { File[] files = dir.listFiles(); if (files != null) { @@ -378,6 +439,27 @@ public class main { .withArgName("API_LEVEL") .create("a"); + Option listMethodsOption = OptionBuilder.withLongOpt("list-methods") + .withDescription("Lists all the method references to FILE" + + " (<output_dex_filename>.methods by default)") + .hasOptionalArg() + .withArgName("FILE") + .create("m"); + + Option listFieldsOption = OptionBuilder.withLongOpt("list-fields") + .withDescription("Lists all the field references to FILE" + + " (<output_dex_filename>.fields by default)") + .hasOptionalArg() + .withArgName("FILE") + .create("f"); + + Option listClassesOption = OptionBuilder.withLongOpt("list-types") + .withDescription("Lists all the type references to FILE" + + " (<output_dex_filename>.types by default)") + .hasOptionalArg() + .withArgName("FILE") + .create("t"); + Option experimentalOption = OptionBuilder.withLongOpt("experimental") .withDescription("enable experimental opcodes to be assembled, even if they " + " aren't necessarily supported by the Android runtime yet") @@ -405,6 +487,9 @@ public class main { basicOptions.addOption(apiLevelOption); basicOptions.addOption(experimentalOption); basicOptions.addOption(jobsOption); + basicOptions.addOption(listMethodsOption); + basicOptions.addOption(listFieldsOption); + basicOptions.addOption(listClassesOption); debugOptions.addOption(verboseErrorsOption); debugOptions.addOption(printTokensOption); |