| /* |
| * 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(); |
| } |
| |
| } |