diff --git a/.gitignore b/.gitignore index 876ae5fa..533e4f04 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ /baksmali/build /dexlib/build /dexlib2/build +/dexlib2/accessorTestGenerator/build /smali/build /util/build diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index c16a6496..d38a76d8 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -33,12 +33,16 @@ import org.jf.baksmali.Adaptors.Debug.DebugMethodItem; import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory; import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.ReferenceType; import org.jf.dexlib2.iface.*; import org.jf.dexlib2.iface.debug.DebugItem; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.OffsetInstruction; +import org.jf.dexlib2.iface.instruction.ReferenceInstruction; +import org.jf.dexlib2.iface.reference.MethodReference; import org.jf.dexlib2.util.InstructionOffsetMap; import org.jf.dexlib2.util.MethodUtil; +import org.jf.dexlib2.util.SyntheticAccessorResolver; import org.jf.dexlib2.util.TypeUtils; import org.jf.util.IndentingWriter; import org.jf.baksmali.baksmali; @@ -314,22 +318,21 @@ public class MethodDefinition { }); } - //TODO: uncomment - /*if (!baksmali.noAccessorComments && (instruction instanceof InstructionWithReference)) { + if (!baksmali.noAccessorComments && (instruction instanceof ReferenceInstruction)) { Opcode opcode = instruction.getOpcode(); - if (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) { - MethodIdItem methodIdItem = - (MethodIdItem)((InstructionWithReference) instruction).getReferencedItem(); - if (SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodIdItem)) { + if (opcode.referenceType == ReferenceType.METHOD) { + MethodReference methodReference = (MethodReference)((ReferenceInstruction)instruction).getReference(); + + if (SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) { SyntheticAccessorResolver.AccessedMember accessedMember = - baksmali.syntheticAccessorResolver.getAccessedMember(methodIdItem); + baksmali.syntheticAccessorResolver.getAccessedMember(methodReference); if (accessedMember != null) { methodItems.add(new SyntheticAccessCommentMethodItem(accessedMember, currentCodeAddress)); } } } - }*/ + } currentCodeAddress += instruction.getCodeUnits(); } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java index 5e6cded0..ef0abb7a 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/SyntheticAccessCommentMethodItem.java @@ -28,17 +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; -//TODO: uncomment -/*public class SyntheticAccessCommentMethodItem extends MethodItem { - private final AccessedMember accessedMember; +public class SyntheticAccessCommentMethodItem extends MethodItem { + private final SyntheticAccessorResolver.AccessedMember accessedMember; - public SyntheticAccessCommentMethodItem(AccessedMember accessedMember, int codeAddress) { + public SyntheticAccessCommentMethodItem(SyntheticAccessorResolver.AccessedMember accessedMember, int codeAddress) { super(codeAddress); this.accessedMember = accessedMember; } @@ -49,15 +49,73 @@ import java.io.IOException; } 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/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java index 83d79d2d..023a23a3 100644 --- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java +++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java @@ -29,9 +29,10 @@ package org.jf.baksmali; import org.jf.baksmali.Adaptors.ClassDefinition; -import org.jf.dexlib.Code.Analysis.*; +import org.jf.dexlib.Code.Analysis.InlineMethodResolver; 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; @@ -118,10 +119,9 @@ public class baksmali { } } - //TODO: uncomment - /*if (!noAccessorComments) { - syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile); - }*/ + if (!noAccessorComments) { + syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile.getClasses()); + } //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 @@ -146,7 +146,7 @@ public class baksmali { * package name are separated by '/' */ - String classDescriptor = classDef.getName(); + String classDescriptor = classDef.getType(); //validate that the descriptor is formatted like we expect if (classDescriptor.charAt(0) != 'L' || diff --git a/dexlib2/accessorTestGenerator/build.gradle b/dexlib2/accessorTestGenerator/build.gradle new file mode 100644 index 00000000..4c344595 --- /dev/null +++ b/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(':util') + compile 'com.google.code.findbugs:jsr305:1.3.9' + compile 'com.google.guava:guava:13.0.1' + compile 'org.antlr:stringtemplate:4.0.2' + + testCompile 'junit:junit:4.6' +} \ No newline at end of file diff --git a/dexlib2/accessorTestGenerator/src/main/java/org/jf/dexlib2/AccessorTestGenerator.java b/dexlib2/accessorTestGenerator/src/main/java/org/jf/dexlib2/AccessorTestGenerator.java new file mode 100644 index 00000000..6540b3df --- /dev/null +++ b/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/dexlib2/accessorTestGenerator/src/main/resources/AccessorTest.stg b/dexlib2/accessorTestGenerator/src/main/resources/AccessorTest.stg new file mode 100644 index 00000000..5c76eecd --- /dev/null +++ b/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/dexlib2/build.gradle b/dexlib2/build.gradle index a009484f..a855e226 100644 --- a/dexlib2/build.gradle +++ b/dexlib2/build.gradle @@ -29,10 +29,91 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +configurations { + accessorTestGenerator +} + dependencies { compile project(':util') compile 'com.google.code.findbugs:jsr305:1.3.9' compile 'com.google.guava:guava:13.0.1' testCompile 'junit:junit:4.6' -} \ No newline at end of file + + 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/dexlib2/src/accessorTest/java/org/jf/dexlib2/AccessorTest.java b/dexlib2/src/accessorTest/java/org/jf/dexlib2/AccessorTest.java new file mode 100644 index 00000000..fad90ac2 --- /dev/null +++ b/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()); + + 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/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java new file mode 100644 index 00000000..bf6631ba --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java @@ -0,0 +1,565 @@ + +// line 1 "SyntheticAccessorFSM.rl" +/* + * 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.util; + +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.formats.Instruction22b; +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; +import org.jf.dexlib2.iface.instruction.WideLiteralInstruction; + +import java.util.List; + +public class SyntheticAccessorFSM { + +// line 43 "SyntheticAccessorFSM.rl" + +// line 48 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" +private static byte[] init__SyntheticAccessorFSM_actions_0() +{ + return new byte [] { + 0, 1, 0, 1, 1, 1, 2, 1, 13, 1, 14, 1, + 15, 1, 16, 1, 17, 1, 18, 1, 19, 1, 20, 1, + 21, 1, 25, 2, 3, 7, 2, 4, 7, 2, 5, 7, + 2, 6, 7, 2, 8, 12, 2, 9, 12, 2, 10, 12, + 2, 11, 12, 2, 22, 23, 2, 22, 24, 2, 22, 25, + 2, 22, 26, 2, 22, 27, 2, 22, 28 + }; +} + +private static final byte _SyntheticAccessorFSM_actions[] = init__SyntheticAccessorFSM_actions_0(); + + +private static short[] init__SyntheticAccessorFSM_key_offsets_0() +{ + return new short [] { + 0, 0, 12, 82, 98, 102, 104, 166, 172, 174, 180, 184, + 190, 192, 196, 198, 201, 203 + }; +} + +private static final short _SyntheticAccessorFSM_key_offsets[] = init__SyntheticAccessorFSM_key_offsets_0(); + + +private static short[] init__SyntheticAccessorFSM_trans_keys_0() +{ + return new short [] { + 82, 88, 89, 95, 96, 102, 103, 109, 110, 114, 116, 120, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 177, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 201, 202, 203, 204, 206, 207, 208, 216, + 15, 17, 18, 25, 129, 143, 144, 176, 178, 205, 144, 145, + 155, 156, 166, 167, 171, 172, 176, 177, 187, 188, 198, 199, + 203, 204, 89, 95, 103, 109, 15, 17, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 177, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 201, 202, 203, 204, 206, 207, 144, 176, 178, 205, 89, 95, + 103, 109, 129, 143, 15, 17, 89, 95, 103, 109, 129, 143, + 89, 95, 103, 109, 89, 95, 103, 109, 129, 143, 15, 17, + 89, 95, 103, 109, 15, 17, 14, 10, 12, 15, 17, 0 + }; +} + +private static final short _SyntheticAccessorFSM_trans_keys[] = init__SyntheticAccessorFSM_trans_keys_0(); + + +private static byte[] init__SyntheticAccessorFSM_single_lengths_0() +{ + return new byte [] { + 0, 0, 60, 16, 0, 0, 58, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0 + }; +} + +private static final byte _SyntheticAccessorFSM_single_lengths[] = init__SyntheticAccessorFSM_single_lengths_0(); + + +private static byte[] init__SyntheticAccessorFSM_range_lengths_0() +{ + return new byte [] { + 0, 6, 5, 0, 2, 1, 2, 3, 1, 3, 2, 3, + 1, 2, 1, 1, 1, 0 + }; +} + +private static final byte _SyntheticAccessorFSM_range_lengths[] = init__SyntheticAccessorFSM_range_lengths_0(); + + +private static short[] init__SyntheticAccessorFSM_index_offsets_0() +{ + return new short [] { + 0, 0, 7, 73, 90, 93, 95, 156, 160, 162, 166, 169, + 173, 175, 178, 180, 183, 185 + }; +} + +private static final short _SyntheticAccessorFSM_index_offsets[] = init__SyntheticAccessorFSM_index_offsets_0(); + + +private static byte[] init__SyntheticAccessorFSM_indicies_0() +{ + return new byte [] { + 0, 2, 0, 2, 3, 3, 1, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 20, 21, 9, 10, 11, 22, 23, 9, + 10, 11, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 10, 11, 12, 13, 14, 15, 16, 17, 20, 21, 10, + 11, 22, 23, 10, 11, 24, 24, 4, 5, 6, 7, 9, + 1, 25, 26, 27, 28, 29, 30, 31, 32, 25, 26, 27, + 28, 29, 30, 31, 32, 1, 33, 33, 1, 34, 1, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 20, 21, 9, 10, + 11, 22, 23, 9, 10, 11, 8, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 10, 11, 12, 13, 14, 15, 16, + 17, 20, 21, 10, 11, 22, 23, 10, 11, 7, 9, 1, + 35, 35, 36, 1, 37, 1, 35, 35, 38, 1, 35, 35, + 1, 39, 39, 40, 1, 41, 1, 39, 39, 1, 42, 1, + 44, 43, 1, 45, 1, 1, 0 + }; +} + +private static final byte _SyntheticAccessorFSM_indicies[] = init__SyntheticAccessorFSM_indicies_0(); + + +private static byte[] init__SyntheticAccessorFSM_trans_targs_0() +{ + return new byte [] { + 2, 0, 14, 15, 17, 3, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 11, 4, 4, 4, 4, 4, 4, 4, 4, 5, 17, 8, + 9, 17, 10, 12, 13, 17, 17, 16, 17, 17 + }; +} + +private static final byte _SyntheticAccessorFSM_trans_targs[] = init__SyntheticAccessorFSM_trans_targs_0(); + + +private static byte[] init__SyntheticAccessorFSM_trans_actions_0() +{ + return new byte [] { + 0, 0, 1, 0, 51, 3, 0, 27, 39, 7, 9, 11, + 13, 15, 17, 19, 21, 23, 30, 42, 33, 45, 36, 48, + 5, 27, 39, 30, 42, 33, 45, 36, 48, 1, 63, 1, + 0, 66, 0, 1, 0, 60, 54, 0, 25, 57 + }; +} + +private static final byte _SyntheticAccessorFSM_trans_actions[] = init__SyntheticAccessorFSM_trans_actions_0(); + + +static final int SyntheticAccessorFSM_start = 1; +static final int SyntheticAccessorFSM_first_final = 17; +static final int SyntheticAccessorFSM_error = 0; + +static final int SyntheticAccessorFSM_en_main = 1; + + +// line 44 "SyntheticAccessorFSM.rl" + + // math type constants + public static final int ADD = SyntheticAccessorResolver.ADD_ASSIGNMENT; + public static final int SUB = SyntheticAccessorResolver.SUB_ASSIGNMENT; + public static final int MUL = SyntheticAccessorResolver.MUL_ASSIGNMENT; + public static final int DIV = SyntheticAccessorResolver.DIV_ASSIGNMENT; + public static final int REM = SyntheticAccessorResolver.REM_ASSIGNMENT; + public static final int AND = SyntheticAccessorResolver.AND_ASSIGNMENT; + public static final int OR = SyntheticAccessorResolver.OR_ASSIGNMENT; + public static final int XOR = SyntheticAccessorResolver.XOR_ASSIGNMENT; + public static final int SHL = SyntheticAccessorResolver.SHL_ASSIGNMENT; + public static final int SHR = SyntheticAccessorResolver.SHR_ASSIGNMENT; + public static final int USHR = SyntheticAccessorResolver.USHR_ASSIGNMENT; + + public static final int INT = 0; + public static final int LONG = 1; + public static final int FLOAT = 2; + public static final int DOUBLE = 3; + + public static final int POSITIVE_ONE = 1; + public static final int NEGATIVE_ONE = -1; + public static final int OTHER = 0; + + public static int test(List instructions) { + int accessorType = -1; + int cs, p = 0; + int pe = instructions.size(); + + // one of the math type constants representing the type of math operation being performed + int mathOp = -1; + + // for increments an decrements, the type of value the math operation is on + int mathType = -1; + + // for increments and decrements, the value of the constant that is used + long constantValue = 0; + + // The source register for the put instruction + int putRegister = -1; + // The return register; + int returnRegister = -1; + + +// line 236 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" + { + cs = SyntheticAccessorFSM_start; + } + +// line 241 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" + { + int _klen; + int _trans = 0; + int _acts; + int _nacts; + int _keys; + int _goto_targ = 0; + + _goto: while (true) { + switch ( _goto_targ ) { + case 0: + if ( p == pe ) { + _goto_targ = 4; + continue _goto; + } + if ( cs == 0 ) { + _goto_targ = 5; + continue _goto; + } +case 1: + _match: do { + _keys = _SyntheticAccessorFSM_key_offsets[cs]; + _trans = _SyntheticAccessorFSM_index_offsets[cs]; + _klen = _SyntheticAccessorFSM_single_lengths[cs]; + if ( _klen > 0 ) { + int _lower = _keys; + int _mid; + int _upper = _keys + _klen - 1; + while (true) { + if ( _upper < _lower ) + break; + + _mid = _lower + ((_upper-_lower) >> 1); + if ( ( instructions.get(p).getOpcode().value) < _SyntheticAccessorFSM_trans_keys[_mid] ) + _upper = _mid - 1; + else if ( ( instructions.get(p).getOpcode().value) > _SyntheticAccessorFSM_trans_keys[_mid] ) + _lower = _mid + 1; + else { + _trans += (_mid - _keys); + break _match; + } + } + _keys += _klen; + _trans += _klen; + } + + _klen = _SyntheticAccessorFSM_range_lengths[cs]; + if ( _klen > 0 ) { + int _lower = _keys; + int _mid; + int _upper = _keys + (_klen<<1) - 2; + while (true) { + if ( _upper < _lower ) + break; + + _mid = _lower + (((_upper-_lower) >> 1) & ~1); + if ( ( instructions.get(p).getOpcode().value) < _SyntheticAccessorFSM_trans_keys[_mid] ) + _upper = _mid - 2; + else if ( ( instructions.get(p).getOpcode().value) > _SyntheticAccessorFSM_trans_keys[_mid+1] ) + _lower = _mid + 2; + else { + _trans += ((_mid - _keys)>>1); + break _match; + } + } + _trans += _klen; + } + } while (false); + + _trans = _SyntheticAccessorFSM_indicies[_trans]; + cs = _SyntheticAccessorFSM_trans_targs[_trans]; + + if ( _SyntheticAccessorFSM_trans_actions[_trans] != 0 ) { + _acts = _SyntheticAccessorFSM_trans_actions[_trans]; + _nacts = (int) _SyntheticAccessorFSM_actions[_acts++]; + while ( _nacts-- > 0 ) + { + switch ( _SyntheticAccessorFSM_actions[_acts++] ) + { + case 0: +// line 94 "SyntheticAccessorFSM.rl" + { + putRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA(); + } + break; + case 1: +// line 101 "SyntheticAccessorFSM.rl" + { + constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral(); + } + break; + case 2: +// line 105 "SyntheticAccessorFSM.rl" + { + mathType = INT; + mathOp = ADD; + constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral(); + } + break; + case 3: +// line 111 "SyntheticAccessorFSM.rl" + { mathType = INT; } + break; + case 4: +// line 112 "SyntheticAccessorFSM.rl" + { mathType = LONG; } + break; + case 5: +// line 113 "SyntheticAccessorFSM.rl" + { mathType = FLOAT; } + break; + case 6: +// line 114 "SyntheticAccessorFSM.rl" + {mathType = DOUBLE; } + break; + case 7: +// line 114 "SyntheticAccessorFSM.rl" + { + mathOp = ADD; + } + break; + case 8: +// line 117 "SyntheticAccessorFSM.rl" + { mathType = INT; } + break; + case 9: +// line 118 "SyntheticAccessorFSM.rl" + { mathType = LONG; } + break; + case 10: +// line 119 "SyntheticAccessorFSM.rl" + { mathType = FLOAT; } + break; + case 11: +// line 120 "SyntheticAccessorFSM.rl" + {mathType = DOUBLE; } + break; + case 12: +// line 120 "SyntheticAccessorFSM.rl" + { + mathOp = SUB; + } + break; + case 13: +// line 124 "SyntheticAccessorFSM.rl" + { + mathOp = MUL; + } + break; + case 14: +// line 128 "SyntheticAccessorFSM.rl" + { + mathOp = DIV; + } + break; + case 15: +// line 132 "SyntheticAccessorFSM.rl" + { + mathOp = REM; + } + break; + case 16: +// line 135 "SyntheticAccessorFSM.rl" + { + mathOp = AND; + } + break; + case 17: +// line 138 "SyntheticAccessorFSM.rl" + { + mathOp = OR; + } + break; + case 18: +// line 141 "SyntheticAccessorFSM.rl" + { + mathOp = XOR; + } + break; + case 19: +// line 144 "SyntheticAccessorFSM.rl" + { + mathOp = SHL; + } + break; + case 20: +// line 147 "SyntheticAccessorFSM.rl" + { + mathOp = SHR; + } + break; + case 21: +// line 150 "SyntheticAccessorFSM.rl" + { + mathOp = USHR; + } + break; + case 22: +// line 156 "SyntheticAccessorFSM.rl" + { + returnRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA(); + } + break; + case 23: +// line 162 "SyntheticAccessorFSM.rl" + { + accessorType = SyntheticAccessorResolver.GETTER; { p += 1; _goto_targ = 5; if (true) continue _goto;} + } + break; + case 24: +// line 166 "SyntheticAccessorFSM.rl" + { + accessorType = SyntheticAccessorResolver.SETTER; { p += 1; _goto_targ = 5; if (true) continue _goto;} + } + break; + case 25: +// line 170 "SyntheticAccessorFSM.rl" + { + accessorType = SyntheticAccessorResolver.METHOD; { p += 1; _goto_targ = 5; if (true) continue _goto;} + } + break; + case 26: +// line 174 "SyntheticAccessorFSM.rl" + { + accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister); + } + break; + case 27: +// line 178 "SyntheticAccessorFSM.rl" + { + accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister); + } + break; + case 28: +// line 186 "SyntheticAccessorFSM.rl" + { + accessorType = mathOp; { p += 1; _goto_targ = 5; if (true) continue _goto;} + } + break; +// line 481 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java" + } + } + } + +case 2: + if ( cs == 0 ) { + _goto_targ = 5; + continue _goto; + } + if ( ++p != pe ) { + _goto_targ = 1; + continue _goto; + } +case 4: +case 5: + } + break; } + } + +// line 199 "SyntheticAccessorFSM.rl" + + + return accessorType; + } + + private static int getIncrementType(int mathOp, int mathType, long constantValue, int putRegister, + int returnRegister) { + boolean isPrefix = putRegister == returnRegister; + + boolean negativeConstant = false; + + switch (mathType) { + case INT: + case LONG: { + if (constantValue == 1) { + negativeConstant = false; + } else if (constantValue == -1) { + negativeConstant = true; + } else { + return -1; + } + break; + } + case FLOAT: { + float val = Float.intBitsToFloat((int)constantValue); + if (val == 1) { + negativeConstant = false; + } else if (val == -1) { + negativeConstant = true; + } else { + return -1; + } + break; + } + case DOUBLE: { + double val = Double.longBitsToDouble(constantValue); + if (val == 1) { + negativeConstant = false; + } else if (val == -1) { + negativeConstant = true; + } else { + return -1; + } + break; + } + } + + boolean isAdd = ((mathOp == ADD) && !negativeConstant) || + ((mathOp == SUB) && negativeConstant); + + if (isPrefix) { + if (isAdd) { + return SyntheticAccessorResolver.PREFIX_INCREMENT; + } else { + return SyntheticAccessorResolver.PREFIX_DECREMENT; + } + } else { + if (isAdd) { + return SyntheticAccessorResolver.POSTFIX_INCREMENT; + } else { + return SyntheticAccessorResolver.POSTFIX_DECREMENT; + } + } + } +} \ No newline at end of file diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java new file mode 100644 index 00000000..248d9bf6 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java @@ -0,0 +1,173 @@ +/* + * 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.util; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.jf.dexlib2.AccessFlags; +import org.jf.dexlib2.iface.ClassDef; +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.*; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; + +public class SyntheticAccessorResolver { + public static final int METHOD = 0; + public static final int GETTER = 1; + public static final int SETTER = 2; + public static final int POSTFIX_INCREMENT = 3; + public static final int PREFIX_INCREMENT = 4; + public static final int POSTFIX_DECREMENT = 5; + public static final int PREFIX_DECREMENT = 6; + public static final int ADD_ASSIGNMENT = 7; + public static final int SUB_ASSIGNMENT = 8; + public static final int MUL_ASSIGNMENT = 9; + public static final int DIV_ASSIGNMENT = 10; + public static final int REM_ASSIGNMENT = 11; + public static final int AND_ASSIGNMENT = 12; + public static final int OR_ASSIGNMENT = 13; + public static final int XOR_ASSIGNMENT = 14; + public static final int SHL_ASSIGNMENT = 15; + public static final int SHR_ASSIGNMENT = 16; + public static final int USHR_ASSIGNMENT = 17; + + + //TODO: use a hashmap for the classes? + private final Map classDefMap; + private final HashMap resolvedAccessors = new HashMap(); + + public SyntheticAccessorResolver(List classDefs) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + + for (ClassDef classDef: classDefs) { + builder.put(classDef.getType(), classDef); + } + + this.classDefMap = builder.build(); + } + + public static boolean looksLikeSyntheticAccessor(String methodName) { + return methodName.startsWith("access$"); + } + + @Nullable + public AccessedMember getAccessedMember(@Nonnull MethodReference methodReference) { + String methodDescriptor = ReferenceUtil.getMethodDescriptor(methodReference); + + AccessedMember accessedMember = resolvedAccessors.get(methodDescriptor); + if (accessedMember != null) { + return accessedMember; + } + + String type = methodReference.getContainingClass(); + ClassDef classDef = classDefMap.get(type); + if (classDef == null) { + return null; + } + + Method matchedMethod = null; + MethodImplementation matchedMethodImpl = null; + for (Method method: classDef.getMethods()) { + MethodImplementation methodImpl = method.getImplementation(); + if (methodImpl != null) { + if (methodReferenceEquals(method, methodReference)) { + matchedMethod = method; + matchedMethodImpl = methodImpl; + break; + } + } + } + + if (matchedMethod == null) { + return null; + } + + //A synthetic accessor will be marked synthetic + if (!AccessFlags.SYNTHETIC.isSet(matchedMethod.getAccessFlags())) { + return null; + } + + List instructions = ImmutableList.copyOf(matchedMethodImpl.getInstructions()); + + int accessType = SyntheticAccessorFSM.test(instructions); + + byte b = 0; + double l = 12341234.567; + b += l; + + if (accessType >= 0) { + return new AccessedMember(accessType, ((ReferenceInstruction)instructions.get(0)).getReference()); + } + return null; + } + + public static class AccessedMember { + public final int accessedMemberType; + @Nonnull public final Reference accessedMember; + + public AccessedMember(int accessedMemberType, @Nonnull Reference accessedMember) { + this.accessedMemberType = accessedMemberType; + this.accessedMember = accessedMember; + } + + @Override + public int hashCode() { + switch (accessedMemberType) { + case METHOD: + + } + return super.hashCode(); //To change body of overridden methods use File | Settings | File Templates. + } + } + + private static boolean methodReferenceEquals(@Nonnull MethodReference ref1, @Nonnull MethodReference ref2) { + // we already know the containing class matches + if ((!ref1.getName().equals(ref2.getName())) || + (!ref1.getReturnType().equals(ref2.getReturnType()))) { + return false; + } + + Iterator params1 = ref1.getParameters().iterator(); + Iterator params2 = ref2.getParameters().iterator(); + while (params1.hasNext() && params2.hasNext()) { + if (!params1.next().getType().equals(params2.next().getType())) { + return false; + } + } + return (!params1.hasNext()) && (!params2.hasNext()); + } +} diff --git a/dexlib2/src/main/ragel/Opcodes.rl b/dexlib2/src/main/ragel/Opcodes.rl new file mode 100644 index 00000000..250c06c9 --- /dev/null +++ b/dexlib2/src/main/ragel/Opcodes.rl @@ -0,0 +1,246 @@ +define nop 0 +define move 1 +define move_from16 2 +define move_16 3 +define move_wide 4 +define move_wide_from16 5 +define move_wide_16 6 +define move_object 7 +define move_object_from16 8 +define move_object_16 9 +define move_result 10 +define move_result_wide 11 +define move_result_object 12 +define move_exception 13 +define return_void 14 +define return 15 +define return_wide 16 +define return_object 17 +define const_4 18 +define const_16 19 +define const 20 +define const_high16 21 +define const_wide_16 22 +define const_wide_32 23 +define const_wide 24 +define const_wide_high16 25 +define const_string 26 +define const_string_jumbo 27 +define const_class 28 +define monitor_enter 29 +define monitor_exit 30 +define check_cast 31 +define instance_of 32 +define array_length 33 +define new_instance 34 +define new_array 35 +define filled_new_array 36 +define filled_new_array_range 37 +define fill_array_data 38 +define throw 39 +define goto 40 +define goto_16 41 +define goto_32 42 +define packed_switch 43 +define sparse_switch 44 +define cmpl_float 45 +define cmpg_float 46 +define cmpl_double 47 +define cmpg_double 48 +define cmp_long 49 +define if_eq 50 +define if_ne 51 +define if_lt 52 +define if_ge 53 +define if_gt 54 +define if_le 55 +define if_eqz 56 +define if_nez 57 +define if_ltz 58 +define if_gez 59 +define if_gtz 60 +define if_lez 61 +define aget 68 +define aget_wide 69 +define aget_object 70 +define aget_boolean 71 +define aget_byte 72 +define aget_char 73 +define aget_short 74 +define aput 75 +define aput_wide 76 +define aput_object 77 +define aput_boolean 78 +define aput_byte 79 +define aput_char 80 +define aput_short 81 +define iget 82 +define iget_wide 83 +define iget_object 84 +define iget_boolean 85 +define iget_byte 86 +define iget_char 87 +define iget_short 88 +define iput 89 +define iput_wide 90 +define iput_object 91 +define iput_boolean 92 +define iput_byte 93 +define iput_char 94 +define iput_short 95 +define sget 96 +define sget_wide 97 +define sget_object 98 +define sget_boolean 99 +define sget_byte 100 +define sget_char 101 +define sget_short 102 +define sput 103 +define sput_wide 104 +define sput_object 105 +define sput_boolean 106 +define sput_byte 107 +define sput_char 108 +define sput_short 109 +define invoke_virtual 110 +define invoke_super 111 +define invoke_direct 112 +define invoke_static 113 +define invoke_interface 114 +define invoke_virtual_range 116 +define invoke_super_range 117 +define invoke_direct_range 118 +define invoke_static_range 119 +define invoke_interface_range 120 +define neg_int 123 +define not_int 124 +define neg_long 125 +define not_long 126 +define neg_float 127 +define neg_double 128 +define int_to_long 129 +define int_to_float 130 +define int_to_double 131 +define long_to_int 132 +define long_to_float 133 +define long_to_double 134 +define float_to_int 135 +define float_to_long 136 +define float_to_double 137 +define double_to_int 138 +define double_to_long 139 +define double_to_float 140 +define int_to_byte 141 +define int_to_char 142 +define int_to_short 143 +define add_int 144 +define sub_int 145 +define mul_int 146 +define div_int 147 +define rem_int 148 +define and_int 149 +define or_int 150 +define xor_int 151 +define shl_int 152 +define shr_int 153 +define ushr_int 154 +define add_long 155 +define sub_long 156 +define mul_long 157 +define div_long 158 +define rem_long 159 +define and_long 160 +define or_long 161 +define xor_long 162 +define shl_long 163 +define shr_long 164 +define ushr_long 165 +define add_float 166 +define sub_float 167 +define mul_float 168 +define div_float 169 +define rem_float 170 +define add_double 171 +define sub_double 172 +define mul_double 173 +define div_double 174 +define rem_double 175 +define add_int_2addr 176 +define sub_int_2addr 177 +define mul_int_2addr 178 +define div_int_2addr 179 +define rem_int_2addr 180 +define and_int_2addr 181 +define or_int_2addr 182 +define xor_int_2addr 183 +define shl_int_2addr 184 +define shr_int_2addr 185 +define ushr_int_2addr 186 +define add_long_2addr 187 +define sub_long_2addr 188 +define mul_long_2addr 189 +define div_long_2addr 190 +define rem_long_2addr 191 +define and_long_2addr 192 +define or_long_2addr 193 +define xor_long_2addr 194 +define shl_long_2addr 195 +define shr_long_2addr 196 +define ushr_long_2addr 197 +define add_float_2addr 198 +define sub_float_2addr 199 +define mul_float_2addr 200 +define div_float_2addr 201 +define rem_float_2addr 202 +define add_double_2addr 203 +define sub_double_2addr 204 +define mul_double_2addr 205 +define div_double_2addr 206 +define rem_double_2addr 207 +define add_int_lit16 208 +define rsub_int 209 +define mul_int_lit16 210 +define div_int_lit16 211 +define rem_int_lit16 212 +define and_int_lit16 213 +define or_int_lit16 214 +define xor_int_lit16 215 +define add_int_lit8 216 +define rsub_int_lit8 217 +define mul_int_lit8 218 +define div_int_lit8 219 +define rem_int_lit8 220 +define and_int_lit8 221 +define or_int_lit8 222 +define xor_int_lit8 223 +define shl_int_lit8 224 +define shr_int_lit8 225 +define ushr_int_lit8 226 +define iget_volatile 227 +define iput_volatile 228 +define sget_volatile 229 +define sput_volatile 230 +define iget_object_volatile 231 +define iget_wide_volatile 232 +define iput_wide_volatile 233 +define sget_wide_volatile 234 +define sput_wide_volatile 235 +define throw_verification_error 237 +define execute_inline 238 +define execute_inline_range 239 +define invoke_direct_empty 240 +define invoke_object_init_range 240 +define return_void_barrier 241 +define iget_quick 242 +define iget_wide_quick 243 +define iget_object_quick 244 +define iput_quick 245 +define iput_wide_quick 246 +define iput_object_quick 247 +define invoke_virtual_quick 248 +define invoke_virtual_quick_range 249 +define invoke_super_quick 250 +define invoke_super_quick_range 251 +define iput_object_volatile 252 +define sget_object_volatile 253 +define sput_object_volatile 254 \ No newline at end of file diff --git a/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl new file mode 100644 index 00000000..d86807ef --- /dev/null +++ b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl @@ -0,0 +1,263 @@ +/* + * 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.util; + +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.formats.Instruction22b; +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; +import org.jf.dexlib2.iface.instruction.WideLiteralInstruction; + +import java.util.List; + +public class SyntheticAccessorFSM { + %% machine SyntheticAccessorFSM; + %% write data; + + // math type constants + public static final int ADD = SyntheticAccessorResolver.ADD_ASSIGNMENT; + public static final int SUB = SyntheticAccessorResolver.SUB_ASSIGNMENT; + public static final int MUL = SyntheticAccessorResolver.MUL_ASSIGNMENT; + public static final int DIV = SyntheticAccessorResolver.DIV_ASSIGNMENT; + public static final int REM = SyntheticAccessorResolver.REM_ASSIGNMENT; + public static final int AND = SyntheticAccessorResolver.AND_ASSIGNMENT; + public static final int OR = SyntheticAccessorResolver.OR_ASSIGNMENT; + public static final int XOR = SyntheticAccessorResolver.XOR_ASSIGNMENT; + public static final int SHL = SyntheticAccessorResolver.SHL_ASSIGNMENT; + public static final int SHR = SyntheticAccessorResolver.SHR_ASSIGNMENT; + public static final int USHR = SyntheticAccessorResolver.USHR_ASSIGNMENT; + + public static final int INT = 0; + public static final int LONG = 1; + public static final int FLOAT = 2; + public static final int DOUBLE = 3; + + public static final int POSITIVE_ONE = 1; + public static final int NEGATIVE_ONE = -1; + public static final int OTHER = 0; + + public static int test(List instructions) { + int accessorType = -1; + int cs, p = 0; + int pe = instructions.size(); + + // one of the math type constants representing the type of math operation being performed + int mathOp = -1; + + // for increments an decrements, the type of value the math operation is on + int mathType = -1; + + // for increments and decrements, the value of the constant that is used + long constantValue = 0; + + // The source register for the put instruction + int putRegister = -1; + // The return register; + int returnRegister = -1; + + %%{ + import "Opcodes.rl"; + alphtype short; + getkey instructions.get(p).getOpcode().value; + + get = (0x52 .. 0x58) | (0x60 .. 0x66); # all igets/sgets + + # all iputs/sputs + put = ((0x59 .. 0x5f) | (0x67 .. 0x6d)) @ { + putRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA(); + }; + + invoke = (0x6e .. 0x72) | (0x74 .. 0x78); # all invokes + + # all numeric const instructions + const_literal = (0x12 .. 0x19) @ { + constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral(); + }; + + add_const = (add_int_lit8 | add_int_lit16) @ { + mathType = INT; + mathOp = ADD; + constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral(); + }; + + arbitrary_add = (((add_int | add_int_2addr) @ { mathType = INT; }) | + ((add_long | add_long_2addr) @ { mathType = LONG; }) | + ((add_float | add_float_2addr) @ { mathType = FLOAT; }) | + ((add_double | add_double_2addr) @ {mathType = DOUBLE; })) @ { + mathOp = ADD; + }; + arbitrary_sub = (((sub_int | sub_int_2addr) @ { mathType = INT; }) | + ((sub_long | sub_long_2addr) @ { mathType = LONG; }) | + ((sub_float | sub_float_2addr) @ { mathType = FLOAT; }) | + ((sub_double | sub_double_2addr) @ {mathType = DOUBLE; })) @ { + mathOp = SUB; + }; + arbitrary_mul = (mul_int | mul_int_2addr | mul_long | mul_long_2addr | + mul_float | mul_float_2addr | mul_double | mul_double_2addr) @ { + mathOp = MUL; + }; + arbitrary_div = (div_int | div_int_2addr | div_long | div_long_2addr | + div_float | div_float_2addr | div_double | div_double_2addr) @ { + mathOp = DIV; + }; + arbitrary_rem = (rem_int | rem_int_2addr | rem_long | rem_long_2addr | + rem_float | rem_float_2addr | rem_double | rem_double_2addr) @ { + mathOp = REM; + }; + arbitrary_and = (and_int | and_int_2addr | and_long | and_long_2addr) @ { + mathOp = AND; + }; + arbitrary_or = (or_int | or_int_2addr | or_long | or_long_2addr) @ { + mathOp = OR; + }; + arbitrary_xor = (xor_int | xor_int_2addr | xor_long | xor_long_2addr) @ { + mathOp = XOR; + }; + arbitrary_shl = (shl_int | shl_int_2addr | shl_long | shl_long_2addr) @ { + mathOp = SHL; + }; + arbitrary_shr = (shr_int | shr_int_2addr | shr_long | shr_long_2addr) @ { + mathOp = SHR; + }; + arbitrary_ushr = (ushr_int | ushr_int_2addr | ushr_long | ushr_long_2addr) @ { + mathOp = USHR; + }; + + type_conversion = 0x81 .. 0x8f; # all type-conversion opcodes + + return_something = (return | return_wide | return_object) @ { + returnRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA(); + }; + + any_move_result = move_result | move_result_wide | move_result_object; + + get_accessor = get return_something @ { + accessorType = SyntheticAccessorResolver.GETTER; fbreak; + }; + + put_accessor = put return_something @ { + accessorType = SyntheticAccessorResolver.SETTER; fbreak; + }; + + invoke_accessor = invoke (return_void | (any_move_result return_something)) @ { + accessorType = SyntheticAccessorResolver.METHOD; fbreak; + }; + + increment_accessor = get add_const type_conversion? put return_something @ { + accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister); + }; + + alt_increment_accessor = get const_literal (arbitrary_add | arbitrary_sub) put return_something @ { + accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister); + }; + + math_assignment_accessor = get type_conversion? + (arbitrary_add | arbitrary_sub | arbitrary_mul | arbitrary_div | arbitrary_rem | + arbitrary_and | arbitrary_or | arbitrary_xor | arbitrary_shl | arbitrary_shr | + arbitrary_ushr) + type_conversion{0,2} put return_something @ { + accessorType = mathOp; fbreak; + }; + + main := get_accessor | + put_accessor | + invoke_accessor | + increment_accessor | + alt_increment_accessor | + math_assignment_accessor; + + write init; + write exec; + }%% + + return accessorType; + } + + private static int getIncrementType(int mathOp, int mathType, long constantValue, int putRegister, + int returnRegister) { + boolean isPrefix = putRegister == returnRegister; + + boolean negativeConstant = false; + + switch (mathType) { + case INT: + case LONG: { + if (constantValue == 1) { + negativeConstant = false; + } else if (constantValue == -1) { + negativeConstant = true; + } else { + return -1; + } + break; + } + case FLOAT: { + float val = Float.intBitsToFloat((int)constantValue); + if (val == 1) { + negativeConstant = false; + } else if (val == -1) { + negativeConstant = true; + } else { + return -1; + } + break; + } + case DOUBLE: { + double val = Double.longBitsToDouble(constantValue); + if (val == 1) { + negativeConstant = false; + } else if (val == -1) { + negativeConstant = true; + } else { + return -1; + } + break; + } + } + + boolean isAdd = ((mathOp == ADD) && !negativeConstant) || + ((mathOp == SUB) && negativeConstant); + + if (isPrefix) { + if (isAdd) { + return SyntheticAccessorResolver.PREFIX_INCREMENT; + } else { + return SyntheticAccessorResolver.PREFIX_DECREMENT; + } + } else { + if (isAdd) { + return SyntheticAccessorResolver.POSTFIX_INCREMENT; + } else { + return SyntheticAccessorResolver.POSTFIX_DECREMENT; + } + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 99fe9907..6b27dcc4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include 'util', 'dexlib', 'dexlib2', 'baksmali', 'smali' \ No newline at end of file +include 'util', 'dexlib', 'dexlib2', 'baksmali', 'smali', 'dexlib2:accessorTestGenerator' \ No newline at end of file