/*
* [The "BSD licence"]
* Copyright (c) 2010 Ben Gruver (JesusFreke)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.baksmali.Adaptors;
import org.jf.baksmali.IndentingWriter;
import org.jf.dexlib.*;
import org.jf.dexlib.Code.Analysis.ValidationException;
import org.jf.dexlib.Code.Format.Instruction21c;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.EncodedValue.EncodedValue;
import org.jf.dexlib.Util.AccessFlags;
import org.jf.dexlib.Util.SparseArray;
import java.io.IOException;
import java.util.List;
public class ClassDefinition {
private ClassDefItem classDefItem;
private ClassDataItem classDataItem;
private SparseArray<AnnotationSetItem> methodAnnotationsMap;
private SparseArray<AnnotationSetItem> fieldAnnotationsMap;
private SparseArray<AnnotationSetRefList> parameterAnnotationsMap;
private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
protected boolean validationErrors;
public ClassDefinition(ClassDefItem classDefItem) {
this.classDefItem = classDefItem;
this.classDataItem = classDefItem.getClassData();
buildAnnotationMaps();
findFieldsSetInStaticConstructor();
}
public boolean hadValidationErrors() {
return validationErrors;
}
private void buildAnnotationMaps() {
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
if (annotationDirectory == null) {
methodAnnotationsMap = new SparseArray<AnnotationSetItem>(0);
fieldAnnotationsMap = new SparseArray<AnnotationSetItem>(0);
parameterAnnotationsMap = new SparseArray<AnnotationSetRefList>(0);
return;
}
methodAnnotationsMap = new SparseArray<AnnotationSetItem>(annotationDirectory.getMethodAnnotationCount());
annotationDirectory.iterateMethodAnnotations(new AnnotationDirectoryItem.MethodAnnotationIteratorDelegate() {
public void processMethodAnnotations(MethodIdItem method, AnnotationSetItem methodAnnotations) {
methodAnnotationsMap.put(method.getIndex(), methodAnnotations);
}
});
fieldAnnotationsMap = new SparseArray<AnnotationSetItem>(annotationDirectory.getFieldAnnotationCount());
annotationDirectory.iterateFieldAnnotations(new AnnotationDirectoryItem.FieldAnnotationIteratorDelegate() {
public void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations) {
fieldAnnotationsMap.put(field.getIndex(), fieldAnnotations);
}
});
parameterAnnotationsMap = new SparseArray<AnnotationSetRefList>(
annotationDirectory.getParameterAnnotationCount());
annotationDirectory.iterateParameterAnnotations(
new AnnotationDirectoryItem.ParameterAnnotationIteratorDelegate() {
public void processParameterAnnotations(MethodIdItem method, AnnotationSetRefList parameterAnnotations) {
parameterAnnotationsMap.put(method.getIndex(), parameterAnnotations);
}
});
}
private void findFieldsSetInStaticConstructor() {
fieldsSetInStaticConstructor = new SparseArray<FieldIdItem>();
if (classDataItem == null) {
return;
}
for (ClassDataItem.EncodedMethod directMethod: classDataItem.getDirectMethods()) {
if (directMethod.method.getMethodName().getStringValue().equals("<clinit>") &&
directMethod.codeItem != null) {
for (Instruction instruction: directMethod.codeItem.getInstructions()) {
switch (instruction.opcode) {
case SPUT:
case SPUT_BOOLEAN:
case SPUT_BYTE:
case SPUT_CHAR:
case SPUT_OBJECT:
case SPUT_SHORT:
case SPUT_WIDE:
Instruction21c ins = (Instruction21c)instruction;
FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
}
}
}
}
}
public void writeTo(IndentingWriter writer) throws IOException {
writeClass(writer);
writeSuper(writer);
writeSourceFile(writer);
writeInterfaces(writer);
writeAnnotations(writer);
writeStaticFields(writer);
writeInstanceFields(writer);
writeDirectMethods(writer);
writeVirtualMethods(writer);
return ;
}
private void writeClass(IndentingWriter writer) throws IOException {
writer.write(".class ");
writeAccessFlags(writer);
writer.write(classDefItem.getClassType().getTypeDescriptor());
writer.write('\n');
}
private void writeAccessFlags(IndentingWriter writer) throws IOException {
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDefItem.getAccessFlags())) {
writer.write(accessFlag.toString());
writer.write(' ');
}
}
private void writeSuper(IndentingWriter writer) throws IOException {
TypeIdItem superClass = classDefItem.getSuperclass();
if (superClass != null) {
writer.write(".super ");
writer.write(superClass.getTypeDescriptor());
writer.write('\n');
}
}
private void writeSourceFile(IndentingWriter writer) throws IOException {
StringIdItem sourceFile = classDefItem.getSourceFile();
if (sourceFile != null) {
writer.write(".source \"");
writer.write(sourceFile.getStringValue());
writer.write("\"\n");
}
}
private void writeInterfaces(IndentingWriter writer) throws IOException {
TypeListItem interfaceList = classDefItem.getInterfaces();
if (interfaceList == null) {
return;
}
List<TypeIdItem> interfaces = interfaceList.getTypes();
if (interfaces == null || interfaces.size() == 0) {
return;
}
writer.write('\n');
writer.write("# interfaces\n");
for (TypeIdItem typeIdItem: interfaceList.getTypes()) {
writer.write(".implements ");
writer.write(typeIdItem.getTypeDescriptor());
writer.write('\n');
}
}
private void writeAnnotations(IndentingWriter writer) throws IOException {
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
if (annotationDirectory == null) {
return;
}
AnnotationSetItem annotationSet = annotationDirectory.getClassAnnotations();
if (annotationSet == null) {
return;
}
writer.write("\n\n");
writer.write("# annotations\n");
AnnotationFormatter.writeTo(writer, annotationSet);
}
private void writeStaticFields(IndentingWriter writer) throws IOException {
if (classDataItem == null) {
return;
}
//if classDataItem is not null, then classDefItem won't be null either
assert(classDefItem != null);
EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers();
EncodedValue[] staticInitializers;
if (encodedStaticInitializers != null) {
staticInitializers = encodedStaticInitializers.getEncodedArray().values;
} else {
staticInitializers = new EncodedValue[0];
}
ClassDataItem.EncodedField[] encodedFields = classDataItem.getStaticFields();
if (encodedFields == null || encodedFields.length == 0) {
return;
}
writer.write("\n\n");
writer.write("# static fields\n");
boolean first = true;
for (int i=0; i<encodedFields.length; i++) {
if (!first) {
writer.write('\n');
}
first = false;
ClassDataItem.EncodedField field = encodedFields[i];
EncodedValue encodedValue = null;
if (i < staticInitializers.length) {
encodedValue = staticInitializers[i];
}
AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
boolean setInStaticConstructor =
fieldsSetInStaticConstructor.get(field.field.getIndex()) != null;
FieldDefinition.writeTo(writer, field, encodedValue, annotationSet, setInStaticConstructor);
}
}
private void writeInstanceFields(IndentingWriter writer) throws IOException {
if (classDataItem == null) {
return;
}
ClassDataItem.EncodedField[] encodedFields = classDataItem.getInstanceFields();
if (encodedFields == null || encodedFields.length == 0) {
return;
}
writer.write("\n\n");
writer.write("# instance fields\n");
boolean first = true;
for (ClassDataItem.EncodedField field: classDataItem.getInstanceFields()) {
if (!first) {
writer.write('\n');
}
first = false;
AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
FieldDefinition.writeTo(writer, field, null, annotationSet, false);
}
}
private void writeDirectMethods(IndentingWriter writer) throws IOException {
if (classDataItem == null) {
return;
}
ClassDataItem.EncodedMethod[] directMethods = classDataItem.getDirectMethods();
if (directMethods == null || directMethods.length == 0) {
return;
}
writer.write("\n\n");
writer.write("# direct methods\n");
writeMethods(writer, directMethods);
}
private void writeVirtualMethods(IndentingWriter writer) throws IOException {
if (classDataItem == null) {
return;
}
ClassDataItem.EncodedMethod[] virtualMethods = classDataItem.getVirtualMethods();
if (virtualMethods == null || virtualMethods.length == 0) {
return;
}
writer.write("\n\n");
writer.write("# virtual methods\n");
writeMethods(writer, virtualMethods);
}
private void writeMethods(IndentingWriter writer, ClassDataItem.EncodedMethod[] methods) throws IOException {
boolean first = true;
for (ClassDataItem.EncodedMethod method: methods) {
if (!first) {
writer.write('\n');
}
first = false;
AnnotationSetItem annotationSet = methodAnnotationsMap.get(method.method.getIndex());
AnnotationSetRefList parameterAnnotationList = parameterAnnotationsMap.get(method.method.getIndex());
MethodDefinition methodDefinition = new MethodDefinition(method);
methodDefinition.writeTo(writer, annotationSet, parameterAnnotationList);
ValidationException validationException = methodDefinition.getValidationException();
if (validationException != null) {
System.err.println(String.format("Error while disassembling method %s. Continuing.",
method.method.getMethodString()));
validationException.printStackTrace(System.err);
this.validationErrors = true;
}
}
}
}