diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java index 69bb240f..c6778c2b 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java @@ -28,16 +28,12 @@ package org.jf.baksmali.Adaptors; +import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.iface.*; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.formats.Instruction21c; import org.jf.util.StringUtils; -import org.jf.util.CommentingIndentingWriter; import org.jf.util.IndentingWriter; -import org.jf.dexlib.*; -import org.jf.dexlib.Code.Analysis.ValidationException; -import org.jf.dexlib.EncodedValue.EncodedValue; -import org.jf.dexlib.Util.AccessFlags; import javax.annotation.Nonnull; import java.io.IOException; @@ -97,7 +93,8 @@ public class ClassDefinition { writeSourceFile(writer); writeInterfaces(writer); writeAnnotations(writer); - writeFields(writer); + writeStaticFields(writer); + writeInstanceFields(writer); /*writeDirectMethods(writer); writeVirtualMethods(writer);*/ } @@ -156,35 +153,39 @@ public class ClassDefinition { } } - private void writeFields(IndentingWriter writer) throws IOException { - List fields = classDef.getFields(); - - boolean wroteStaticFieldHeader = false; - boolean wroteInstanceFieldHeader = false; - for (Field field: fields) { - //TODO: add an isStatic field to field? or somehow make this better - boolean isStatic = (field.getAccessFlags() & AccessFlags.STATIC.getValue()) != 0; - - if (isStatic) { - if (!wroteStaticFieldHeader) { + private void writeStaticFields(IndentingWriter writer) throws IOException { + boolean wroteHeader = false; + for (Field field: classDef.getFields()) { + if (AccessFlags.STATIC.isSet(field.getAccessFlags())) { + if (!wroteHeader) { writer.write("\n\n"); writer.write("# static fields"); - wroteStaticFieldHeader = true; + wroteHeader = true; } - } else { - if (!wroteInstanceFieldHeader) { + writer.write('\n'); + // TODO: detect duplicate fields. + // TODO: check if field is set in static constructor + + FieldDefinition.writeTo(writer, field, false); + } + } + } + + private void writeInstanceFields(IndentingWriter writer) throws IOException { + boolean wroteHeader = false; + for (Field field: classDef.getFields()) { + if (!AccessFlags.STATIC.isSet(field.getAccessFlags())) { + if (!wroteHeader) { writer.write("\n\n"); writer.write("# instance fields"); - wroteInstanceFieldHeader = true; + wroteHeader = true; } + writer.write('\n'); + // TODO: detect duplicate fields. + // TODO: check if field is set in static constructor + + FieldDefinition.writeTo(writer, field, false); } - - writer.write('\n'); - - // TODO: detect duplicate fields. - // TODO: check if field is set in static constructor - - FieldDefinition.writeTo(writer, field, false); } } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/FieldDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/FieldDefinition.java index ef0ce793..0b608b28 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/FieldDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/FieldDefinition.java @@ -29,12 +29,12 @@ package org.jf.baksmali.Adaptors; import org.jf.baksmali.Adaptors.EncodedValue.EncodedValueAdaptor; +import org.jf.dexlib2.AccessFlags; +import org.jf.dexlib2.ValueType; import org.jf.dexlib2.iface.Annotation; import org.jf.dexlib2.iface.Field; import org.jf.dexlib2.iface.value.EncodedValue; import org.jf.util.IndentingWriter; -import org.jf.dexlib.EncodedValue.NullEncodedValue; -import org.jf.dexlib.Util.AccessFlags; import java.io.IOException; import java.util.List; @@ -42,15 +42,15 @@ import java.util.List; public class FieldDefinition { public static void writeTo(IndentingWriter writer, Field field, boolean setInStaticConstructor) throws IOException { EncodedValue initialValue = field.getInitialValue(); + int accessFlags = field.getAccessFlags(); if (setInStaticConstructor && - ((field.getAccessFlags() & AccessFlags.STATIC.getValue()) != 0) && - ((field.getAccessFlags() & AccessFlags.FINAL.getValue()) != 0) && - field.getInitialValue() != null && - ( - //it's a primitive type, or it's an array/reference type and the initial value isn't null + AccessFlags.STATIC.isSet(accessFlags) && + AccessFlags.FINAL.isSet(accessFlags) && + initialValue != null && + ( //it's a primitive type, or it's an array/reference type and the initial value isn't null field.getType().length() == 1 || - initialValue != NullEncodedValue.NullValue + initialValue.getValueType() != ValueType.NULL )) { writer.write("#the value of this static final field might be set in the static constructor\n"); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/AccessFlags.java b/dexlib2/src/main/java/org/jf/dexlib2/AccessFlags.java new file mode 100644 index 00000000..d3cf74c8 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/AccessFlags.java @@ -0,0 +1,185 @@ +/* + * 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 java.util.HashMap; + +public enum AccessFlags +{ + PUBLIC(0x1, "public", true, true, true), + PRIVATE(0x2, "private", true, true, true), + PROTECTED(0x4, "protected", true, true, true), + STATIC(0x8, "static", true, true, true), + FINAL(0x10, "final", true, true, true), + SYNCHRONIZED(0x20, "synchronized", false, true, false), + VOLATILE(0x40, "volatile", false, false, true), + BRIDGE(0x40, "bridge", false, true, false), + TRANSIENT(0x80, "transient", false, false, true), + VARARGS(0x80, "varargs", false, true, false), + NATIVE(0x100, "native", false, true, false), + INTERFACE(0x200, "interface", true, false, false), + ABSTRACT(0x400, "abstract", true, true, false), + STRICTFP(0x800, "strictfp", false, true, false), + SYNTHETIC(0x1000, "synthetic", true, true, true), + ANNOTATION(0x2000, "annotation", true, false, false), + ENUM(0x4000, "enum", true, false, true), + CONSTRUCTOR(0x10000, "constructor", false, true, false), + DECLARED_SYNCHRONIZED(0x20000, "declared-synchronized", false, true, false); + + private int value; + private String accessFlagName; + private boolean validForClass; + private boolean validForMethod; + private boolean validForField; + + //cache the array of all AccessFlags, because .values() allocates a new array for every call + private final static AccessFlags[] allFlags; + + private static HashMap accessFlagsByName; + + static { + allFlags = AccessFlags.values(); + + accessFlagsByName = new HashMap(); + for (AccessFlags accessFlag: allFlags) { + accessFlagsByName.put(accessFlag.accessFlagName, accessFlag); + } + } + + private AccessFlags(int value, String accessFlagName, boolean validForClass, boolean validForMethod, + boolean validForField) { + this.value = value; + this.accessFlagName = accessFlagName; + this.validForClass = validForClass; + this.validForMethod = validForMethod; + this.validForField = validForField; + } + + public boolean isSet(int accessFlags) { + return (this.value & accessFlags) != 0; + } + + public static AccessFlags[] getAccessFlagsForClass(int accessFlagValue) { + int size = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForClass && (accessFlagValue & accessFlag.value) != 0) { + size++; + } + } + + AccessFlags[] accessFlags = new AccessFlags[size]; + int accessFlagsPosition = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForClass && (accessFlagValue & accessFlag.value) != 0) { + accessFlags[accessFlagsPosition++] = accessFlag; + } + } + return accessFlags; + } + + private static String formatAccessFlags(AccessFlags[] accessFlags) { + int size = 0; + for (AccessFlags accessFlag: accessFlags) { + size += accessFlag.toString().length() + 1; + } + + StringBuilder sb = new StringBuilder(size); + for (AccessFlags accessFlag: accessFlags) { + sb.append(accessFlag.toString()); + sb.append(" "); + } + if (accessFlags.length > 0) { + sb.delete(sb.length() - 1, sb.length()); + } + return sb.toString(); + } + + public static String formatAccessFlagsForClass(int accessFlagValue) { + return formatAccessFlags(getAccessFlagsForClass(accessFlagValue)); + } + + public static AccessFlags[] getAccessFlagsForMethod(int accessFlagValue) { + int size = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForMethod && (accessFlagValue & accessFlag.value) != 0) { + size++; + } + } + + AccessFlags[] accessFlags = new AccessFlags[size]; + int accessFlagsPosition = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForMethod && (accessFlagValue & accessFlag.value) != 0) { + accessFlags[accessFlagsPosition++] = accessFlag; + } + } + return accessFlags; + } + + public static String formatAccessFlagsForMethod(int accessFlagValue) { + return formatAccessFlags(getAccessFlagsForMethod(accessFlagValue)); + } + + public static AccessFlags[] getAccessFlagsForField(int accessFlagValue) { + int size = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForField && (accessFlagValue & accessFlag.value) != 0) { + size++; + } + } + + AccessFlags[] accessFlags = new AccessFlags[size]; + int accessFlagsPosition = 0; + for (AccessFlags accessFlag: allFlags) { + if (accessFlag.validForField && (accessFlagValue & accessFlag.value) != 0) { + accessFlags[accessFlagsPosition++] = accessFlag; + } + } + return accessFlags; + } + + public static String formatAccessFlagsForField(int accessFlagValue) { + return formatAccessFlags(getAccessFlagsForField(accessFlagValue)); + } + + public static AccessFlags getAccessFlag(String accessFlag) { + return accessFlagsByName.get(accessFlag); + } + + public int getValue() { + return value; + } + + public String toString() { + return accessFlagName; + } +}