Implement implicit method/field references in baksmali

This commit is contained in:
Ben Gruver 2014-07-19 20:23:32 -07:00 committed by Connor Tumbleson
parent 2772be8e9d
commit 1b0a917a6a
17 changed files with 557 additions and 58 deletions

View File

@ -0,0 +1,322 @@
/*
* Copyright 2014, 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 junit.framework.Assert;
import org.antlr.runtime.RecognitionException;
import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.smali.SmaliTestUtils;
import org.jf.util.IndentingWriter;
import org.jf.util.TextUtils;
import org.junit.Test;
import java.io.IOException;
import java.io.StringWriter;
public class ImplicitReferenceTest {
@Test
public void testImplicitMethodReferences() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" invoke-static {p0}, LHelloWorld;->toString()V\n" +
" invoke-static {p0}, LHelloWorld;->V()V\n" +
" invoke-static {p0}, LHelloWorld;->I()V\n" +
" return-void\n" +
".end method");
String expected = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
"# direct methods\n" +
".method public static main([Ljava/lang/String;)V\n" +
".registers 1\n" +
"invoke-static {p0}, toString()V\n" +
"invoke-static {p0}, V()V\n" +
"invoke-static {p0}, I()V\n" +
"return-void\n" +
".end method\n";
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
}
@Test
public void testExplicitMethodReferences() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" invoke-static {p0}, LHelloWorld;->toString()V\n" +
" invoke-static {p0}, LHelloWorld;->V()V\n" +
" invoke-static {p0}, LHelloWorld;->I()V\n" +
" return-void\n" +
".end method");
String expected = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
"# direct methods\n" +
".method public static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" invoke-static {p0}, LHelloWorld;->toString()V\n" +
" invoke-static {p0}, LHelloWorld;->V()V\n" +
" invoke-static {p0}, LHelloWorld;->I()V\n" +
" return-void\n" +
".end method\n";
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
}
@Test
public void testImplicitMethodLiterals() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
".field public static field4:Ljava/lang/Class; = I");
String expected = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
"# static fields\n" +
".field public static field1:Ljava/lang/reflect/Method; = toString()V\n" +
".field public static field2:Ljava/lang/reflect/Method; = V()V\n" +
".field public static field3:Ljava/lang/reflect/Method; = I()V\n" +
".field public static field4:Ljava/lang/Class; = I\n";
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
}
@Test
public void testExplicitMethodLiterals() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
".field public static field4:Ljava/lang/Class; = I");
String expected = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
"# static fields\n" +
".field public static field1:Ljava/lang/reflect/Method; = LHelloWorld;->toString()V\n" +
".field public static field2:Ljava/lang/reflect/Method; = LHelloWorld;->V()V\n" +
".field public static field3:Ljava/lang/reflect/Method; = LHelloWorld;->I()V\n" +
".field public static field4:Ljava/lang/Class; = I\n";
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
}
@Test
public void testImplicitFieldReferences() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" sget v0, LHelloWorld;->someField:I\n" +
" sget v0, LHelloWorld;->I:I\n" +
" sget v0, LHelloWorld;->V:I\n" +
" return-void\n" +
".end method");
String expected = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
"# direct methods\n" +
".method public static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" sget p0, someField:I\n" +
" sget p0, I:I\n" +
" sget p0, V:I\n" +
" return-void\n" +
".end method\n";
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
}
@Test
public void testExplicitFieldReferences() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".method public static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" sget v0, LHelloWorld;->someField:I\n" +
" sget v0, LHelloWorld;->I:I\n" +
" sget v0, LHelloWorld;->V:I\n" +
" return-void\n" +
".end method");
String expected = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
"# direct methods\n" +
".method public static main([Ljava/lang/String;)V\n" +
" .registers 1\n" +
" sget p0, LHelloWorld;->someField:I\n" +
" sget p0, LHelloWorld;->I:I\n" +
" sget p0, LHelloWorld;->V:I\n" +
" return-void\n" +
".end method\n";
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
}
@Test
public void testImplicitFieldLiterals() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I");
String expected = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
"# static fields\n" +
".field public static field1:Ljava/lang/reflect/Field; = someField:I\n" +
".field public static field2:Ljava/lang/reflect/Field; = V:I\n" +
".field public static field3:Ljava/lang/reflect/Field; = I:I\n";
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = true;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
}
@Test
public void testExplicitFieldLiterals() throws IOException, RecognitionException {
ClassDef classDef = SmaliTestUtils.compileSmali("" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I");
String expected = "" +
".class public LHelloWorld;\n" +
".super Ljava/lang/Object;\n" +
"# static fields\n" +
".field public static field1:Ljava/lang/reflect/Field; = LHelloWorld;->someField:I\n" +
".field public static field2:Ljava/lang/reflect/Field; = LHelloWorld;->V:I\n" +
".field public static field3:Ljava/lang/reflect/Field; = LHelloWorld;->I:I\n";
baksmaliOptions options = new baksmaliOptions();
options.useImplicitReferences = false;
StringWriter stringWriter = new StringWriter();
IndentingWriter writer = new IndentingWriter(stringWriter);
ClassDefinition classDefinition = new ClassDefinition(options, classDef);
classDefinition.writeTo(writer);
writer.close();
Assert.assertEquals(TextUtils.normalizeWhitespace(expected),
TextUtils.normalizeWhitespace(stringWriter.toString()));
}
}

View File

@ -40,6 +40,7 @@ dependencies {
compile depends.guava
testCompile depends.junit
testCompile project(':smali')
proguard depends.proguard
}

View File

@ -33,13 +33,16 @@ import org.jf.dexlib2.AnnotationVisibility;
import org.jf.dexlib2.iface.Annotation;
import org.jf.util.IndentingWriter;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Collection;
public class AnnotationFormatter {
public static void writeTo(IndentingWriter writer,
Collection<? extends Annotation> annotations) throws IOException {
public static void writeTo(@Nonnull IndentingWriter writer,
@Nonnull Collection<? extends Annotation> annotations,
@Nullable String containingClass) throws IOException {
boolean first = true;
for (Annotation annotation: annotations) {
if (!first) {
@ -47,18 +50,19 @@ public class AnnotationFormatter {
}
first = false;
writeTo(writer, annotation);
writeTo(writer, annotation, containingClass);
}
}
public static void writeTo(IndentingWriter writer, Annotation annotation) throws IOException {
public static void writeTo(@Nonnull IndentingWriter writer, @Nonnull Annotation annotation,
@Nullable String containingClass) throws IOException {
writer.write(".annotation ");
writer.write(AnnotationVisibility.getVisibility(annotation.getVisibility()));
writer.write(' ');
writer.write(annotation.getType());
writer.write('\n');
AnnotationEncodedValueAdaptor.writeElementsTo(writer, annotation.getElements());
AnnotationEncodedValueAdaptor.writeElementsTo(writer, annotation.getElements(), containingClass);
writer.write(".end annotation\n");
}

View File

@ -165,7 +165,13 @@ public class ClassDefinition {
if (classAnnotations.size() != 0) {
writer.write("\n\n");
writer.write("# annotations\n");
AnnotationFormatter.writeTo(writer, classAnnotations);
String containingClass = null;
if (options.useImplicitReferences) {
containingClass = classDef.getType();
}
AnnotationFormatter.writeTo(writer, classAnnotations, containingClass);
}
}
@ -199,7 +205,7 @@ public class ClassDefinition {
} else {
setInStaticConstructor = fieldsSetInStaticConstructor.contains(fieldString);
}
FieldDefinition.writeTo(fieldWriter, field, setInStaticConstructor);
FieldDefinition.writeTo(options, fieldWriter, field, setInStaticConstructor);
}
return writtenFields;
}
@ -237,7 +243,7 @@ public class ClassDefinition {
writer.write("# There is both a static and instance field with this signature.\n" +
"# You will need to rename one of these fields, including all references.\n");
}
FieldDefinition.writeTo(fieldWriter, field, false);
FieldDefinition.writeTo(options, fieldWriter, field, false);
}
}
@ -261,7 +267,7 @@ public class ClassDefinition {
writer.write('\n');
// TODO: check for method validation errors
String methodString = ReferenceUtil.getShortMethodDescriptor(method);
String methodString = ReferenceUtil.getMethodDescriptor(method, true);
IndentingWriter methodWriter = writer;
if (!writtenMethods.add(methodString)) {
@ -300,7 +306,7 @@ public class ClassDefinition {
writer.write('\n');
// TODO: check for method validation errors
String methodString = ReferenceUtil.getShortMethodDescriptor(method);
String methodString = ReferenceUtil.getMethodDescriptor(method, true);
IndentingWriter methodWriter = writer;
if (!writtenMethods.add(methodString)) {

View File

@ -32,28 +32,32 @@ import org.jf.dexlib2.iface.AnnotationElement;
import org.jf.dexlib2.iface.value.AnnotationEncodedValue;
import org.jf.util.IndentingWriter;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Collection;
public abstract class AnnotationEncodedValueAdaptor {
public static void writeTo(IndentingWriter writer, AnnotationEncodedValue annotationEncodedValue)
throws IOException {
public static void writeTo(@Nonnull IndentingWriter writer,
@Nonnull AnnotationEncodedValue annotationEncodedValue,
@Nullable String containingClass) throws IOException {
writer.write(".subannotation ");
writer.write(annotationEncodedValue.getType());
writer.write('\n');
writeElementsTo(writer, annotationEncodedValue.getElements());
writeElementsTo(writer, annotationEncodedValue.getElements(), containingClass);
writer.write(".end subannotation");
}
public static void writeElementsTo(IndentingWriter writer,
Collection<? extends AnnotationElement> annotationElements) throws IOException {
public static void writeElementsTo(@Nonnull IndentingWriter writer,
@Nonnull Collection<? extends AnnotationElement> annotationElements,
@Nullable String containingClass) throws IOException {
writer.indent(4);
for (AnnotationElement annotationElement: annotationElements) {
writer.write(annotationElement.getName());
writer.write(" = ");
EncodedValueAdaptor.writeTo(writer, annotationElement.getValue());
EncodedValueAdaptor.writeTo(writer, annotationElement.getValue(), containingClass);
writer.write('\n');
}
writer.deindent(4);

View File

@ -28,15 +28,19 @@
package org.jf.baksmali.Adaptors.EncodedValue;
import org.jf.util.IndentingWriter;
import org.jf.dexlib2.iface.value.ArrayEncodedValue;
import org.jf.dexlib2.iface.value.EncodedValue;
import org.jf.util.IndentingWriter;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Collection;
public class ArrayEncodedValueAdaptor {
public static void writeTo(IndentingWriter writer, ArrayEncodedValue arrayEncodedValue) throws IOException {
public static void writeTo(@Nonnull IndentingWriter writer,
@Nonnull ArrayEncodedValue arrayEncodedValue,
@Nullable String containingClass) throws IOException {
writer.write('{');
Collection<? extends EncodedValue> values = arrayEncodedValue.getValue();
if (values.size() == 0) {
@ -53,7 +57,7 @@ public class ArrayEncodedValueAdaptor {
}
first = false;
EncodedValueAdaptor.writeTo(writer, encodedValue);
EncodedValueAdaptor.writeTo(writer, encodedValue, containingClass);
}
writer.deindent(4);
writer.write("\n}");

View File

@ -29,22 +29,26 @@
package org.jf.baksmali.Adaptors.EncodedValue;
import org.jf.baksmali.Adaptors.ReferenceFormatter;
import org.jf.baksmali.Renderers.*;
import org.jf.dexlib2.ValueType;
import org.jf.dexlib2.iface.value.*;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.IndentingWriter;
import org.jf.baksmali.Renderers.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
public abstract class EncodedValueAdaptor {
public static void writeTo(IndentingWriter writer, EncodedValue encodedValue) throws IOException {
public static void writeTo(@Nonnull IndentingWriter writer, @Nonnull EncodedValue encodedValue,
@Nullable String containingClass)
throws IOException {
switch (encodedValue.getValueType()) {
case ValueType.ANNOTATION:
AnnotationEncodedValueAdaptor.writeTo(writer, (AnnotationEncodedValue)encodedValue);
AnnotationEncodedValueAdaptor.writeTo(writer, (AnnotationEncodedValue)encodedValue, containingClass);
return;
case ValueType.ARRAY:
ArrayEncodedValueAdaptor.writeTo(writer, (ArrayEncodedValue)encodedValue);
ArrayEncodedValueAdaptor.writeTo(writer, (ArrayEncodedValue)encodedValue, containingClass);
return;
case ValueType.BOOLEAN:
BooleanRenderer.writeTo(writer, ((BooleanEncodedValue)encodedValue).getValue());
@ -59,11 +63,21 @@ public abstract class EncodedValueAdaptor {
DoubleRenderer.writeTo(writer, ((DoubleEncodedValue)encodedValue).getValue());
return;
case ValueType.ENUM:
EnumEncodedValue enumEncodedValue = (EnumEncodedValue)encodedValue;
boolean useImplicitReference = false;
if (enumEncodedValue.getValue().getDefiningClass().equals(containingClass)) {
useImplicitReference = true;
}
writer.write(".enum ");
ReferenceUtil.writeFieldDescriptor(writer, ((EnumEncodedValue)encodedValue).getValue());
ReferenceUtil.writeFieldDescriptor(writer, enumEncodedValue.getValue(), useImplicitReference);
return;
case ValueType.FIELD:
ReferenceUtil.writeFieldDescriptor(writer, ((FieldEncodedValue)encodedValue).getValue());
FieldEncodedValue fieldEncodedValue = (FieldEncodedValue)encodedValue;
useImplicitReference = false;
if (fieldEncodedValue.getValue().getDefiningClass().equals(containingClass)) {
useImplicitReference = true;
}
ReferenceUtil.writeFieldDescriptor(writer, fieldEncodedValue.getValue(), useImplicitReference);
return;
case ValueType.FLOAT:
FloatRenderer.writeTo(writer, ((FloatEncodedValue)encodedValue).getValue());
@ -75,7 +89,12 @@ public abstract class EncodedValueAdaptor {
LongRenderer.writeTo(writer, ((LongEncodedValue)encodedValue).getValue());
return;
case ValueType.METHOD:
ReferenceUtil.writeMethodDescriptor(writer, ((MethodEncodedValue)encodedValue).getValue());
MethodEncodedValue methodEncodedValue = (MethodEncodedValue)encodedValue;
useImplicitReference = false;
if (methodEncodedValue.getValue().getDefiningClass().equals(containingClass)) {
useImplicitReference = true;
}
ReferenceUtil.writeMethodDescriptor(writer, methodEncodedValue.getValue(), useImplicitReference);
return;
case ValueType.NULL:
writer.write("null");

View File

@ -29,6 +29,7 @@
package org.jf.baksmali.Adaptors;
import org.jf.baksmali.Adaptors.EncodedValue.EncodedValueAdaptor;
import org.jf.baksmali.baksmaliOptions;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.Field;
@ -40,7 +41,8 @@ import java.io.IOException;
import java.util.Collection;
public class FieldDefinition {
public static void writeTo(IndentingWriter writer, Field field, boolean setInStaticConstructor) throws IOException {
public static void writeTo(baksmaliOptions options, IndentingWriter writer, Field field,
boolean setInStaticConstructor) throws IOException {
EncodedValue initialValue = field.getInitialValue();
int accessFlags = field.getAccessFlags();
@ -64,7 +66,13 @@ public class FieldDefinition {
writer.write(field.getType());
if (initialValue != null) {
writer.write(" = ");
EncodedValueAdaptor.writeTo(writer, initialValue);
String containingClass = null;
if (options.useImplicitReferences) {
containingClass = field.getDefiningClass();
}
EncodedValueAdaptor.writeTo(writer, initialValue, containingClass);
}
writer.write('\n');
@ -72,7 +80,13 @@ public class FieldDefinition {
Collection<? extends Annotation> annotations = field.getAnnotations();
if (annotations.size() > 0) {
writer.indent(4);
AnnotationFormatter.writeTo(writer, annotations);
String containingClass = null;
if (options.useImplicitReferences) {
containingClass = field.getDefiningClass();
}
AnnotationFormatter.writeTo(writer, annotations, containingClass);
writer.deindent(4);
writer.write(".end field\n");
}

View File

@ -41,6 +41,8 @@ import org.jf.dexlib2.iface.instruction.*;
import org.jf.dexlib2.iface.instruction.formats.Instruction20bc;
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
import org.jf.dexlib2.iface.instruction.formats.UnknownInstruction;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.iface.reference.Reference;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
@ -102,7 +104,13 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
ReferenceInstruction referenceInstruction = (ReferenceInstruction)instruction;
try {
Reference reference = referenceInstruction.getReference();
referenceString = ReferenceUtil.getReferenceString(reference);
String classContext = null;
if (methodDef.classDef.options.useImplicitReferences) {
classContext = methodDef.method.getDefiningClass();
}
referenceString = ReferenceUtil.getReferenceString(reference, classContext);
assert referenceString != null;
} catch (InvalidItemIndex ex) {
writer.write("#");

View File

@ -56,6 +56,7 @@ import org.jf.util.IndentingWriter;
import org.jf.util.SparseIntArray;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.*;
@ -148,7 +149,13 @@ public class MethodDefinition {
writer.indent(4);
writeParameters(writer, method, methodParameters, options);
AnnotationFormatter.writeTo(writer, method.getAnnotations());
String containingClass = null;
if (options.useImplicitReferences) {
containingClass = method.getDefiningClass();
}
AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass);
writer.deindent(4);
writer.write(".end method\n");
}
@ -191,7 +198,11 @@ public class MethodDefinition {
parameterRegisterCount);
}
AnnotationFormatter.writeTo(writer, method.getAnnotations());
String containingClass = null;
if (classDef.options.useImplicitReferences) {
containingClass = method.getDefiningClass();
}
AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass);
writer.write('\n');
@ -264,7 +275,12 @@ public class MethodDefinition {
writer.write("\n");
if (annotations.size() > 0) {
writer.indent(4);
AnnotationFormatter.writeTo(writer, annotations);
String containingClass = null;
if (options.useImplicitReferences) {
containingClass = method.getDefiningClass();
}
AnnotationFormatter.writeTo(writer, annotations, containingClass);
writer.deindent(4);
writer.write(".end param\n");
}
@ -519,6 +535,14 @@ public class MethodDefinition {
}
}
@Nullable
private String getContainingClassForImplicitReference() {
if (classDef.options.useImplicitReferences) {
return classDef.classDef.getType();
}
return null;
}
public static class LabelCache {
protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>();

View File

@ -74,6 +74,7 @@ public class baksmaliOptions {
public boolean deodex = false;
public boolean ignoreErrors = false;
public boolean checkPackagePrivateAccess = false;
public boolean useImplicitReferences = true;
public File customInlineDefinitions = null;
public InlineMethodResolver inlineResolver = null;
public int registerInfo = 0;

View File

@ -205,6 +205,9 @@ public class main {
String rif = commandLine.getOptionValue("i");
options.setResourceIdFiles(rif);
break;
case 't':
options.useImplicitReferences = false;
break;
case 'N':
disassemble = false;
break;
@ -420,6 +423,10 @@ public class main {
.withArgName("FILES")
.create("i");
Option noImplicitReferencesOption = OptionBuilder.withLongOpt("no-implicit-references")
.withDescription("Don't use implicit (type-less) method and field references")
.create("t");
Option dumpOption = OptionBuilder.withLongOpt("dump-to")
.withDescription("dumps the given dex file into a single annotated dump file named FILE" +
" (<dexfile>.dump by default), along with the normal disassembly")
@ -459,6 +466,7 @@ public class main {
basicOptions.addOption(apiLevelOption);
basicOptions.addOption(jobsOption);
basicOptions.addOption(resourceIdFilesOption);
basicOptions.addOption(noImplicitReferencesOption);
debugOptions.addOption(dumpOption);
debugOptions.addOption(ignoreErrorsOption);

View File

@ -91,6 +91,7 @@ public class AnalysisTest {
options.registerInfo = baksmaliOptions.ALL | baksmaliOptions.FULLMERGE;
options.classPath = new ClassPath();
}
options.useImplicitReferences = false;
for (ClassDef classDef: dexFile.getClasses()) {
StringWriter stringWriter = new StringWriter();

View File

@ -1637,7 +1637,7 @@ public class MethodAnalyzer {
String superclass = methodClass.getSuperclass();
if (superclass == null) {
throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s",
ReferenceUtil.getShortMethodDescriptor(resolvedMethod));
ReferenceUtil.getMethodDescriptor(resolvedMethod, true));
}
methodClass = classPath.getClassDef(superclass);
@ -1648,7 +1648,7 @@ public class MethodAnalyzer {
resolvedMethod = classPath.getClass(methodClass.getType()).getMethodByVtableIndex(methodIndex);
if (resolvedMethod == null) {
throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s",
ReferenceUtil.getShortMethodDescriptor(resolvedMethod));
ReferenceUtil.getMethodDescriptor(resolvedMethod, true));
}
resolvedMethod = new ImmutableMethodReference(methodClass.getType(), resolvedMethod.getName(),
resolvedMethod.getParameterTypes(), resolvedMethod.getReturnType());

View File

@ -34,27 +34,22 @@ package org.jf.dexlib2.util;
import org.jf.dexlib2.iface.reference.*;
import org.jf.util.StringUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.Writer;
public final class ReferenceUtil {
public static String getShortMethodDescriptor(MethodReference methodReference) {
StringBuilder sb = new StringBuilder();
sb.append(methodReference.getName());
sb.append('(');
for (CharSequence paramType: methodReference.getParameterTypes()) {
sb.append(paramType);
}
sb.append(')');
sb.append(methodReference.getReturnType());
return sb.toString();
public static String getMethodDescriptor(MethodReference methodReference) {
return getMethodDescriptor(methodReference, false);
}
public static String getMethodDescriptor(MethodReference methodReference) {
public static String getMethodDescriptor(MethodReference methodReference, boolean useImplicitReference) {
StringBuilder sb = new StringBuilder();
sb.append(methodReference.getDefiningClass());
sb.append("->");
if (!useImplicitReference) {
sb.append(methodReference.getDefiningClass());
sb.append("->");
}
sb.append(methodReference.getName());
sb.append('(');
for (CharSequence paramType: methodReference.getParameterTypes()) {
@ -66,8 +61,15 @@ public final class ReferenceUtil {
}
public static void writeMethodDescriptor(Writer writer, MethodReference methodReference) throws IOException {
writer.write(methodReference.getDefiningClass());
writer.write("->");
writeMethodDescriptor(writer, methodReference, false);
}
public static void writeMethodDescriptor(Writer writer, MethodReference methodReference,
boolean useImplicitReference) throws IOException {
if (!useImplicitReference) {
writer.write(methodReference.getDefiningClass());
writer.write("->");
}
writer.write(methodReference.getName());
writer.write('(');
for (CharSequence paramType: methodReference.getParameterTypes()) {
@ -78,9 +80,15 @@ public final class ReferenceUtil {
}
public static String getFieldDescriptor(FieldReference fieldReference) {
return getFieldDescriptor(fieldReference, false);
}
public static String getFieldDescriptor(FieldReference fieldReference, boolean useImplicitReference) {
StringBuilder sb = new StringBuilder();
sb.append(fieldReference.getDefiningClass());
sb.append("->");
if (!useImplicitReference) {
sb.append(fieldReference.getDefiningClass());
sb.append("->");
}
sb.append(fieldReference.getName());
sb.append(':');
sb.append(fieldReference.getType());
@ -96,15 +104,27 @@ public final class ReferenceUtil {
}
public static void writeFieldDescriptor(Writer writer, FieldReference fieldReference) throws IOException {
writer.write(fieldReference.getDefiningClass());
writer.write("->");
writeFieldDescriptor(writer, fieldReference, false);
}
public static void writeFieldDescriptor(Writer writer, FieldReference fieldReference,
boolean implicitReference) throws IOException {
if (!implicitReference) {
writer.write(fieldReference.getDefiningClass());
writer.write("->");
}
writer.write(fieldReference.getName());
writer.write(':');
writer.write(fieldReference.getType());
}
@Nullable
public static String getReferenceString(Reference reference) {
public static String getReferenceString(@Nonnull Reference reference) {
return getReferenceString(reference, null);
}
@Nullable
public static String getReferenceString(@Nonnull Reference reference, @Nullable String containingClass) {
if (reference instanceof StringReference) {
return String.format("\"%s\"", StringUtils.escapeString(((StringReference)reference).getString()));
}
@ -112,10 +132,14 @@ public final class ReferenceUtil {
return ((TypeReference)reference).getType();
}
if (reference instanceof FieldReference) {
return getFieldDescriptor((FieldReference)reference);
FieldReference fieldReference = (FieldReference)reference;
boolean useImplicitReference = fieldReference.getDefiningClass().equals(containingClass);
return getFieldDescriptor((FieldReference)reference, useImplicitReference);
}
if (reference instanceof MethodReference) {
return getMethodDescriptor((MethodReference)reference);
MethodReference methodReference = (MethodReference)reference;
boolean useImplicitReference = methodReference.getDefiningClass().equals(containingClass);
return getMethodDescriptor((MethodReference)reference, useImplicitReference);
}
return null;
}

View File

@ -116,7 +116,7 @@ public class ClassPool implements ClassSection<CharSequence, CharSequence,
HashSet<String> methods = new HashSet<String>();
for (PoolMethod method: poolClassDef.getMethods()) {
String methodDescriptor = ReferenceUtil.getShortMethodDescriptor(method);
String methodDescriptor = ReferenceUtil.getMethodDescriptor(method, true);
if (!methods.add(methodDescriptor)) {
throw new ExceptionWithContext("Multiple definitions for method %s->%s",
poolClassDef.getType(), methodDescriptor);

View File

@ -0,0 +1,59 @@
/*
* Copyright 2014, 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.util;
import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TextUtils {
private static String newline = System.getProperty("line.separator");
@Nonnull
public static String normalizeNewlines(@Nonnull String source) {
return normalizeNewlines(source, newline);
}
@Nonnull
public static String normalizeNewlines(@Nonnull String source, String newlineValue) {
return source.replace("\r", "").replace("\n", newlineValue);
}
@Nonnull
public static String normalizeWhitespace(@Nonnull String source) {
source = normalizeNewlines(source, "\n");
Pattern pattern = Pattern.compile("(\n[ \t]*)+");
Matcher matcher = pattern.matcher(source);
return matcher.replaceAll("\n");
}
}