/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dex.reader;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import dex.reader.DexFileReader.ClassDefItem;
import dex.reader.DexFileReader.FieldIdItem;
import dex.reader.DexFileReader.MethodsIdItem;
import dex.reader.DexFileReader.ProtIdItem;
import dex.structure.DexAnnotation;
import dex.structure.DexClass;
import dex.structure.DexField;
import dex.structure.DexMethod;
/* package */final class DexClassImpl implements DexClass {
// constant
private final int NO_INDEX = -1;
// dex bytes
private final DexBuffer buffer;
// allready parsed
private final ClassDefItem classDefItem;
private final int[] typeIds;
private final String[] stringPool;
private ProtIdItem[] protoIdItems;
private FieldIdItem[] fieldIdItems;
private MethodsIdItem[] methodIdItems;
//
private List<DexField> fields;
private List<DexMethod> methods;
private List<String> interfaces;
private ClassDataItem classDataItem;
private AnnotationsDirectoryItem annotationDir;
private Map<Integer, FieldAnnotation> idToFieldAnnotation =
new HashMap<Integer, FieldAnnotation>();
private Map<Integer, MethodAnnotation> idToMethodAnnotation =
new HashMap<Integer, MethodAnnotation>();
private Map<Integer, ParameterAnnotation> idToParameterAnnotation =
new HashMap<Integer, ParameterAnnotation>();
private Set<DexAnnotation> annotations;
private TypeFormatter formatter = new TypeFormatter();
private boolean hasClassData;
public DexClassImpl(DexBuffer buffer, ClassDefItem classDefItem,
String[] stringPool, int[] typeIds, ProtIdItem[] protoIdItems,
FieldIdItem[] fieldIdItems, MethodsIdItem[] methodIdItems) {
this.buffer = buffer;
this.classDefItem = classDefItem;
this.stringPool = stringPool;
this.typeIds = typeIds;
this.protoIdItems = protoIdItems;
this.fieldIdItems = fieldIdItems;
this.methodIdItems = methodIdItems;
hasClassData = classDefItem.class_data_off != 0;
parseClassData();
parseAnnotationDirectory();
parseClassAnnotations();
}
static class AnnotationsDirectoryItem {
int class_annotations_off; // uint
int fields_size; // uint
int methods_size; // uint
int annotated_params_size; // uint
FieldAnnotation[] fieldAnnotations;
MethodAnnotation[] methodAnnotations;
ParameterAnnotation[] parameterAnnotations;
}
static class AnnotationSetItem {
int size;// uint
int[] annotationOffItem;
}
static class FieldAnnotation {
int fieldIdx;// uint
int annotationsOff;// uint
AnnotationSetItem[] annotationSetItems;
}
static class MethodAnnotation {
int methodIdx;// uint
int annotationsOff;// uint
AnnotationSetItem[] annotationSetItems;
}
static class ParameterAnnotation {
int methodIdx;// uint
int annotationsOff;// uint
// AnnotationSetRefListItem[] annotationSetRefListItems;
}
private void parseAnnotationDirectory() {
if (classDefItem.annotations_off != 0) {
buffer.setPosition(classDefItem.annotations_off);
annotationDir = new AnnotationsDirectoryItem();
annotationDir.class_annotations_off = buffer.readUInt();
annotationDir.fields_size = buffer.readUInt();
annotationDir.methods_size = buffer.readUInt();
annotationDir.annotated_params_size = buffer.readUInt();
if (annotationDir.fields_size != 0) {
annotationDir.fieldAnnotations =
new FieldAnnotation[annotationDir.fields_size];
for (int i = 0; i < annotationDir.fields_size; i++) {
annotationDir.fieldAnnotations[i] = new FieldAnnotation();
annotationDir.fieldAnnotations[i].fieldIdx = buffer
.readUInt();
annotationDir.fieldAnnotations[i].annotationsOff = buffer
.readUInt();
idToFieldAnnotation.put(
annotationDir.fieldAnnotations[i].fieldIdx,
annotationDir.fieldAnnotations[i]);
}
}
if (annotationDir.methods_size != 0) {
annotationDir.methodAnnotations =
new MethodAnnotation[annotationDir.methods_size];
for (int i = 0; i < annotationDir.methods_size; i++) {
annotationDir.methodAnnotations[i] = new MethodAnnotation();
annotationDir.methodAnnotations[i].methodIdx = buffer
.readUInt();
annotationDir.methodAnnotations[i].annotationsOff = buffer
.readUInt();
idToMethodAnnotation.put(
annotationDir.methodAnnotations[i].methodIdx,
annotationDir.methodAnnotations[i]);
}
}
if (annotationDir.annotated_params_size != 0) {
annotationDir.parameterAnnotations =
new ParameterAnnotation[annotationDir
.annotated_params_size];
for (int i = 0; i < annotationDir.annotated_params_size; i++) {
annotationDir.parameterAnnotations[i] =
new ParameterAnnotation();
annotationDir.parameterAnnotations[i].methodIdx = buffer
.readUInt();
annotationDir.parameterAnnotations[i].annotationsOff =
buffer.readUInt();
idToParameterAnnotation.put(
annotationDir.parameterAnnotations[i].methodIdx,
annotationDir.parameterAnnotations[i]);
}
}
}
}
static class ClassDataItem {
int static_fields_size;// uleb128
int instance_fields_size;// uleb128
int direct_methods_size;// uleb128
int virtual_methods_size;// uleb128
EncodedField[] staticFields;
EncodedField[] instanceFields;
EncodedMethod[] directMethods;
EncodedMethod[] virtualMethods;
}
static class EncodedField {
int field_idx_diff; // uleb128
int access_flags; // uleb128
}
static class EncodedMethod {
int method_idx_diff;// uleb128
int access_flags;// uleb128
int code_off; // uleb128
}
private void parseClassData() {
if (hasClassData) {
buffer.setPosition(classDefItem.class_data_off);
classDataItem = new ClassDataItem();
classDataItem.static_fields_size = buffer.readUleb128();
classDataItem.instance_fields_size = buffer.readUleb128();
classDataItem.direct_methods_size = buffer.readUleb128();
classDataItem.virtual_methods_size = buffer.readUleb128();
classDataItem.staticFields = parseFields(
classDataItem.static_fields_size);
classDataItem.instanceFields = parseFields(
classDataItem.instance_fields_size);
classDataItem.directMethods = parseMethods(
classDataItem.direct_methods_size);
classDataItem.virtualMethods = parseMethods(
classDataItem.virtual_methods_size);
}
}
private EncodedField[] parseFields(int size) {
EncodedField[] fields = new EncodedField[size];
for (int i = 0; i < fields.length; i++) {
fields[i] = new EncodedField();
fields[i].field_idx_diff = buffer.readUleb128();
fields[i].access_flags = buffer.readUleb128();
}
return fields;
}
private EncodedMethod[] parseMethods(int size) {
EncodedMethod[] methods = new EncodedMethod[size];
for (int i = 0; i < methods.length; i++) {
methods[i] = new EncodedMethod();
methods[i].method_idx_diff = buffer.readUleb128();
methods[i].access_flags = buffer.readUleb128();
methods[i].code_off = buffer.readUleb128();
}
return methods;
}
private void parseClassAnnotations() {
annotations = new HashSet<DexAnnotation>();
if (annotationDir != null && annotationDir.class_annotations_off != 0) {
buffer.setPosition(annotationDir.class_annotations_off);
final int size = buffer.readUInt();
for (int i = 0; i < size; i++) {
annotations.add(new DexAnnotationImpl(buffer.createCopy(),
buffer.readUInt(), typeIds, stringPool, fieldIdItems));
}
}
}
public synchronized List<DexField> getFields() {
if (fields == null) {
fields = new ArrayList<DexField>();
if (hasClassData) {
fields.addAll(getDexFields(classDataItem.staticFields));
fields.addAll(getDexFields(classDataItem.instanceFields));
}
}
return fields;
}
private List<DexField> getDexFields(EncodedField[] fields) {
List<DexField> dexFields = new ArrayList<DexField>(fields.length);
if (fields.length != 0) {
int fieldIdIdx = 0;
for (int i = 0; i < fields.length; i++) {
int accessFlags = fields[i].access_flags;
fieldIdIdx = (i == 0) ? fields[i].field_idx_diff : fieldIdIdx
+ fields[i].field_idx_diff;
dexFields.add(new DexFieldImpl(buffer.createCopy(), this,
fieldIdItems[fieldIdIdx], accessFlags,
idToFieldAnnotation.get(fieldIdIdx), stringPool,
typeIds, fieldIdItems));
}
}
return dexFields;
}
public synchronized List<DexMethod> getMethods() {
if (methods == null) {
methods = new ArrayList<DexMethod>();
if (hasClassData) {
methods.addAll(getDexMethods(classDataItem.directMethods));
methods.addAll(getDexMethods(classDataItem.virtualMethods));
}
}
return methods;
}
private List<DexMethod> getDexMethods(EncodedMethod[] methods) {
List<DexMethod> dexMethods = new ArrayList<DexMethod>(methods.length);
if (methods.length != 0) {
int methodIdIdx = 0;
EncodedMethod method = null;
for (int i = 0; i < methods.length; i++) {
method = methods[i];
methodIdIdx = (i == 0) ? method.method_idx_diff : methodIdIdx
+ method.method_idx_diff;
dexMethods.add(new DexMethodImpl(buffer, this,
methodIdItems[methodIdIdx],
protoIdItems[methodIdItems[methodIdIdx].proto_idx],
method.access_flags, idToMethodAnnotation
.get(methodIdIdx), idToParameterAnnotation
.get(methodIdIdx), stringPool, typeIds,
fieldIdItems));
}
}
return dexMethods;
}
public synchronized List<String> getInterfaces() {
if (interfaces == null) {
interfaces = new LinkedList<String>();
if (classDefItem.interfaces_off != 0) {
buffer.setPosition(classDefItem.interfaces_off);
int size = buffer.readUInt();
for (int i = 0; i < size; i++) {
interfaces.add(stringPool[typeIds[buffer.readUShort()]]);
}
}
}
return interfaces;
}
// returns null if no super class is present
public String getSuperClass() {
return classDefItem.superclass_idx == NO_INDEX ? null
: stringPool[typeIds[classDefItem.superclass_idx]];
}
public Set<DexAnnotation> getAnnotations() {
return annotations;
}
public String getName() {
return stringPool[typeIds[classDefItem.class_idx]];
}
public int getModifiers() {
return classDefItem.access_flags;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(formatter.formatAnnotations(getAnnotations()));
builder.append(Modifier.toString(getModifiers()));
builder.append(" class ");
builder.append(formatter.format(getName()));
if (getSuperClass() != null) {
builder.append(" extends ");
builder.append(formatter.format(getSuperClass()));
}
if (!getInterfaces().isEmpty()) {
builder.append(" implements ");
builder.append(formatter.format(getInterfaces()));
}
return builder.toString();
}
}