/* * 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 fields; private List methods; private List interfaces; private ClassDataItem classDataItem; private AnnotationsDirectoryItem annotationDir; private Map idToFieldAnnotation = new HashMap(); private Map idToMethodAnnotation = new HashMap(); private Map idToParameterAnnotation = new HashMap(); private Set 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(); 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 getFields() { if (fields == null) { fields = new ArrayList(); if (hasClassData) { fields.addAll(getDexFields(classDataItem.staticFields)); fields.addAll(getDexFields(classDataItem.instanceFields)); } } return fields; } private List getDexFields(EncodedField[] fields) { List dexFields = new ArrayList(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 getMethods() { if (methods == null) { methods = new ArrayList(); if (hasClassData) { methods.addAll(getDexMethods(classDataItem.directMethods)); methods.addAll(getDexMethods(classDataItem.virtualMethods)); } } return methods; } private List getDexMethods(EncodedMethod[] methods) { List dexMethods = new ArrayList(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 getInterfaces() { if (interfaces == null) { interfaces = new LinkedList(); 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 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(); } }