| /* |
| * 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 signature.converter.dex; |
| |
| import static signature.converter.dex.DexUtil.convertAnyWay; |
| import static signature.converter.dex.DexUtil.declaresExceptions; |
| import static signature.converter.dex.DexUtil.declaresMemberClasses; |
| import static signature.converter.dex.DexUtil.findPackageInfo; |
| import static signature.converter.dex.DexUtil.getClassModifiers; |
| import static signature.converter.dex.DexUtil.getClassName; |
| import static signature.converter.dex.DexUtil.getDefaultMappingsAnnotation; |
| import static signature.converter.dex.DexUtil.getDexName; |
| import static signature.converter.dex.DexUtil.getEnclosingClassName; |
| import static signature.converter.dex.DexUtil.getExceptionSignature; |
| import static signature.converter.dex.DexUtil.getGenericSignature; |
| import static signature.converter.dex.DexUtil.getKind; |
| import static signature.converter.dex.DexUtil.getMemberClassNames; |
| import static signature.converter.dex.DexUtil.getModifier; |
| import static signature.converter.dex.DexUtil.getPackageName; |
| import static signature.converter.dex.DexUtil.getQualifiedName; |
| import static signature.converter.dex.DexUtil.hasAnnotationDefaultSignature; |
| import static signature.converter.dex.DexUtil.hasGenericSignature; |
| import static signature.converter.dex.DexUtil.isAnnotation; |
| import static signature.converter.dex.DexUtil.isConstructor; |
| import static signature.converter.dex.DexUtil.isEnclosingClass; |
| import static signature.converter.dex.DexUtil.isEnum; |
| import static signature.converter.dex.DexUtil.isInternalAnnotation; |
| import static signature.converter.dex.DexUtil.isJavaLangObject; |
| import static signature.converter.dex.DexUtil.isMethod; |
| import static signature.converter.dex.DexUtil.isVisible; |
| import static signature.converter.dex.DexUtil.splitTypeList; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import signature.converter.Visibility; |
| import signature.model.IAnnotation; |
| import signature.model.IAnnotationElement; |
| import signature.model.IAnnotationField; |
| import signature.model.IClassDefinition; |
| import signature.model.IClassReference; |
| import signature.model.IConstructor; |
| import signature.model.IEnumConstant; |
| import signature.model.IField; |
| import signature.model.IMethod; |
| import signature.model.IPackage; |
| import signature.model.IParameter; |
| import signature.model.ITypeReference; |
| import signature.model.ITypeVariableDefinition; |
| import signature.model.Kind; |
| import signature.model.Modifier; |
| import signature.model.impl.SigAnnotation; |
| import signature.model.impl.SigAnnotationElement; |
| import signature.model.impl.SigAnnotationField; |
| import signature.model.impl.SigApi; |
| import signature.model.impl.SigClassDefinition; |
| import signature.model.impl.SigClassReference; |
| import signature.model.impl.SigConstructor; |
| import signature.model.impl.SigEnumConstant; |
| import signature.model.impl.SigExecutableMember; |
| import signature.model.impl.SigField; |
| import signature.model.impl.SigMethod; |
| import signature.model.impl.SigPackage; |
| import signature.model.impl.SigParameter; |
| import signature.model.impl.Uninitialized; |
| import signature.model.util.TypePool; |
| import dex.structure.DexAnnotation; |
| import dex.structure.DexAnnotationAttribute; |
| import dex.structure.DexClass; |
| import dex.structure.DexEncodedAnnotation; |
| import dex.structure.DexEncodedValue; |
| import dex.structure.DexField; |
| import dex.structure.DexFile; |
| import dex.structure.DexMethod; |
| import dex.structure.DexParameter; |
| |
| /** |
| * Converts a set of dex files to the signature compare api. |
| */ |
| public final class DexToSigConverter implements IClassInitializer { |
| |
| private final FieldPool elementPool; |
| private final TypePool factory; |
| private static final Set<IField> EMPTY_FIELDS = Collections.emptySet(); |
| private static final Set<IEnumConstant> EMPTY_ENUM_CONSTANTS = Collections |
| .emptySet(); |
| private static final Set<IAnnotationField> EMPTY_ANNOTATION_FIELDS = |
| Collections.emptySet(); |
| private static final List<ITypeVariableDefinition> EMPTY_TYPE_VARIABLES = |
| Collections.emptyList(); |
| private static final Set<IClassDefinition> EMPTY_INNER_CLASSES = |
| Collections.emptySet(); |
| private static final Set<ITypeReference> EMPTY_EXCEPTIONS = Collections |
| .emptySet(); |
| private Visibility visibility; |
| private Map<String, DexClass> dexNameToDexClass; |
| |
| |
| /** |
| * Creates a new instance of {@link DexToSigConverter}. |
| */ |
| public DexToSigConverter() { |
| factory = new TypePool(); |
| elementPool = new FieldPool(); |
| } |
| |
| |
| public SigApi convertApi(String apiName, Set<DexFile> dexFiles, |
| Visibility visibility) { |
| this.visibility = visibility; |
| SigApi api = new SigApi(apiName, visibility); |
| api.setPackages(convertPackages(dexFiles)); |
| factory.replaceAllUninitialiezWithNull(); |
| return api; |
| } |
| |
| /** |
| * Converts the given {@link DexFile}s into the corresponding (packages |
| * including their (classes and their members, etc.))E |
| * |
| * @param parsedFiles |
| * the dex files to convert |
| * @return the converted packages |
| */ |
| /* package */Set<IPackage> convertPackages(Set<DexFile> parsedFiles) { |
| Map<String, SigPackage> packageNameToPackage = |
| new HashMap<String, SigPackage>(); |
| Map<SigPackage, Set<DexClass>> packageToDexClasses = |
| new HashMap<SigPackage, Set<DexClass>>(); |
| |
| dexNameToDexClass = new HashMap<String, DexClass>(); |
| |
| for (DexFile dexFile : parsedFiles) { |
| List<DexClass> definedClasses = dexFile.getDefinedClasses(); |
| for (DexClass dexClass : definedClasses) { |
| |
| dexNameToDexClass.put(dexClass.getName(), dexClass); |
| |
| String dexName = dexClass.getName(); |
| String packageName = getPackageName(dexName); |
| SigPackage aPackage = packageNameToPackage.get(packageName); |
| if (aPackage == null) { |
| aPackage = convertPackage(packageName); |
| packageNameToPackage.put(packageName, aPackage); |
| |
| Set<DexClass> classes = new HashSet<DexClass>(); |
| packageToDexClasses.put(aPackage, classes); |
| } |
| Set<DexClass> classes = packageToDexClasses.get(aPackage); |
| classes.add(dexClass); |
| } |
| } |
| |
| Set<SigClassDefinition> allClasses = new HashSet<SigClassDefinition>(); |
| |
| for (SigPackage aPackage : packageToDexClasses.keySet()) { |
| Set<SigClassDefinition> classes = convertClasses(packageToDexClasses |
| .get(aPackage)); |
| allClasses.addAll(classes); |
| aPackage.setClasses(new HashSet<IClassDefinition>(classes)); |
| } |
| |
| // remove package info |
| for (SigPackage aPackage : packageToDexClasses.keySet()) { |
| IClassDefinition packageInfo = findPackageInfo(aPackage); |
| if (packageInfo != null) { |
| aPackage.setAnnotations(packageInfo.getAnnotations()); |
| aPackage.getClasses().remove(packageInfo); |
| } |
| } |
| |
| // link enclosed classes only if they are part of visible api |
| for (SigClassDefinition sigClass : allClasses) { |
| String dexName = getDexName(sigClass); |
| DexClass dexClass = dexNameToDexClass.get(dexName); |
| |
| if (declaresMemberClasses(dexClass)) { |
| Set<String> enclosedClassesNames = |
| getMemberClassNames(dexClass); |
| Set<IClassDefinition> memberClasses = |
| new HashSet<IClassDefinition>(); |
| for (String enclosedClassName : enclosedClassesNames) { |
| SigClassDefinition memberClass = factory.getClass( |
| getPackageName(enclosedClassName), |
| getClassName(enclosedClassName)); |
| // add inner class only if parsed |
| if (allClasses.contains(memberClass)) { |
| memberClasses.add(memberClass); |
| } |
| } |
| sigClass.setInnerClasses(memberClasses); |
| } else { |
| sigClass.setInnerClasses(EMPTY_INNER_CLASSES); |
| } |
| } |
| |
| // remove inner classes, is outer class is not visible |
| for (SigClassDefinition sigClass : allClasses) { |
| if (hasInvisibleParent(sigClass, dexNameToDexClass)) { |
| SigPackage sigPackage = packageNameToPackage.get(sigClass |
| .getPackageName()); |
| sigPackage.getClasses().remove(sigClass); |
| } |
| } |
| return new HashSet<IPackage>(packageToDexClasses.keySet()); |
| } |
| |
| private boolean hasInvisibleParent(IClassDefinition sigClass, |
| Map<String, DexClass> dexNameToDexClass) { |
| |
| do { |
| String dexName = getDexName(sigClass); |
| DexClass dexClass = dexNameToDexClass.get(dexName); |
| if (isEnclosingClass(dexClass)) { |
| IClassDefinition declaringClass = sigClass.getDeclaringClass(); |
| DexClass declaringDexClass = dexNameToDexClass |
| .get(getDexName(declaringClass)); |
| if (!isVisible(declaringDexClass, visibility)) { |
| return true; |
| } |
| } |
| } while ((sigClass = sigClass.getDeclaringClass()) != null); |
| return false; |
| } |
| |
| /** |
| * Converts a simple string to the corresponding {@link SigPackage}.<br> |
| * Format: "a.b.c" |
| * |
| * @param packageName |
| * the name of the package |
| * @return the package |
| */ |
| protected SigPackage convertPackage(String packageName) { |
| SigPackage sigPackage = new SigPackage(packageName); |
| return sigPackage; |
| } |
| |
| /** |
| * Converts a set of {@link DexClass} objects to a set of the corresponding |
| * {@link SigClassDefinition} objects. |
| * |
| * @param dexClasses |
| * the {@link DexClass} objects |
| * @return a set of {@link DexClass} objects |
| */ |
| protected Set<SigClassDefinition> convertClasses(Set<DexClass> dexClasses) { |
| Set<SigClassDefinition> classes = new HashSet<SigClassDefinition>(); |
| for (DexClass dexClass : dexClasses) { |
| // convert all classes but synthetic, return only initialized |
| if (convertAnyWay(dexClass)) { |
| SigClassDefinition sigCLass = convertClass(dexClass); |
| if (isVisible(dexClass, visibility)) { |
| classes.add(sigCLass); |
| } |
| } |
| } |
| return classes; |
| } |
| |
| /** |
| * Converts a {@link DexClass} to the corresponding |
| * {@link SigClassDefinition}. |
| * |
| * @param dexClass |
| * the {@link DexClass} to convert |
| * @return the corresponding {@link SigClassDefinition} |
| */ |
| protected SigClassDefinition convertClass(DexClass dexClass) { |
| assert dexClass != null; |
| |
| String packageName = getPackageName(dexClass.getName()); |
| String className = getClassName(dexClass.getName()); |
| SigClassDefinition sigClass = factory.getClass(packageName, className); |
| // Kind |
| sigClass.setKind(getKind(dexClass)); |
| // modifiers |
| Set<Modifier> modifiers = getModifier(getClassModifiers(dexClass)); |
| sigClass.setModifiers(modifiers); |
| |
| if (isEnclosingClass(dexClass)) { |
| String declaringClassDexName = getEnclosingClassName(dexClass); |
| declaringClassDexName = getClassName(declaringClassDexName); |
| // declaring class is in same package |
| sigClass.setDeclaringClass(factory.getClass(sigClass |
| .getPackageName(), declaringClassDexName)); |
| } else { |
| sigClass.setDeclaringClass(null); |
| } |
| |
| if (hasGenericSignature(dexClass)) { |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| parser.parseForClass(sigClass, getGenericSignature(dexClass)); |
| sigClass.setTypeParameters(parser.formalTypeParameters); |
| |
| if (Kind.INTERFACE.equals(sigClass.getKind())) { |
| sigClass.setSuperClass(null); |
| } else { |
| sigClass.setSuperClass(parser.superclassType); |
| } |
| |
| sigClass.setInterfaces(new HashSet<ITypeReference>( |
| parser.interfaceTypes)); |
| } else { |
| |
| // Type parameters |
| sigClass.setTypeParameters(EMPTY_TYPE_VARIABLES); |
| |
| // java.lang.Object has no super class |
| if (isJavaLangObject(dexClass)) { |
| sigClass.setSuperClass(null); |
| } else { |
| |
| if (Kind.INTERFACE.equals(sigClass.getKind()) |
| || Kind.ANNOTATION.equals(sigClass.getKind())) { |
| sigClass.setSuperClass(null); |
| } else { |
| String superClassPackageName = getPackageName(dexClass |
| .getSuperClass()); |
| String superClassName = getClassName(dexClass |
| .getSuperClass()); |
| sigClass.setSuperClass(factory.getClassReference( |
| superClassPackageName, superClassName)); |
| } |
| } |
| |
| List<String> interfaceDexNames = dexClass.getInterfaces(); |
| Set<ITypeReference> interfaces = new HashSet<ITypeReference>(); |
| for (String interfaceDexName : interfaceDexNames) { |
| String interfacePackageName = getPackageName(interfaceDexName); |
| String interfaceName = getClassName(interfaceDexName); |
| SigClassDefinition interfaze = factory.getClass( |
| interfacePackageName, interfaceName); |
| interfaze.setKind(Kind.INTERFACE); |
| interfaces.add(new SigClassReference(interfaze)); |
| } |
| sigClass.setInterfaces(interfaces); |
| } |
| |
| // constructors |
| Set<SigConstructor> constructors = convertConstructors(dexClass |
| .getMethods()); |
| for (SigConstructor constructor : constructors) { |
| constructor.setDeclaringClass(sigClass); |
| } |
| sigClass.setConstructors(new HashSet<IConstructor>(constructors)); |
| |
| // methods |
| Set<SigMethod> methods = Collections.emptySet(); |
| |
| |
| if (isAnnotation(dexClass)) { |
| Map<String, Object> mappings = getDefaultValueMapping(dexClass); |
| Set<SigAnnotationField> annotationFields = convertAnnotationFields( |
| dexClass.getMethods(), mappings); |
| sigClass.setAnnotationFields(new HashSet<IAnnotationField>( |
| annotationFields)); |
| addAnnotationsToAnnotationFields(dexClass.getMethods(), |
| annotationFields); |
| |
| sigClass.setEnumConstants(EMPTY_ENUM_CONSTANTS); |
| sigClass.setFields(EMPTY_FIELDS); |
| |
| // sigClass.setAnnotationFields(new |
| // HashSet<IAnnotationField>(convertAnnotationFields(dexClass))); |
| } else if (isEnum(dexClass)) { |
| Set<IField> fields = new HashSet<IField>(); |
| Set<IEnumConstant> enumConstants = new HashSet<IEnumConstant>(); |
| |
| for (DexField dexField : dexClass.getFields()) { |
| if (isVisible(dexField, visibility)) { |
| if (dexField.isEnumConstant()) { |
| enumConstants.add(convertEnumConstant(dexField)); |
| } else { |
| fields.add(convertField(dexField)); |
| } |
| } |
| } |
| |
| sigClass.setFields(fields); |
| sigClass.setEnumConstants(enumConstants); |
| sigClass.setAnnotationFields(EMPTY_ANNOTATION_FIELDS); |
| methods = convertMethods(dexClass.getMethods()); |
| } else { |
| // fields |
| sigClass.setFields(new HashSet<IField>(convertFields(dexClass |
| .getFields()))); |
| sigClass.setEnumConstants(EMPTY_ENUM_CONSTANTS); |
| sigClass.setAnnotationFields(EMPTY_ANNOTATION_FIELDS); |
| methods = convertMethods(dexClass.getMethods()); |
| } |
| |
| for (SigMethod method : methods) { |
| method.setDeclaringClass(sigClass); |
| } |
| sigClass.setMethods(new HashSet<IMethod>(methods)); |
| |
| // Annotations |
| sigClass.setAnnotations(convertAnnotations(dexClass.getAnnotations())); |
| |
| return sigClass; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Map<String, Object> getDefaultValueMapping(DexClass dexClass) { |
| HashMap<String, Object> mappings = new HashMap<String, Object>(); |
| if (hasAnnotationDefaultSignature(dexClass)) { |
| // read mapping to defaults from annotation |
| DexAnnotation annotation = getDefaultMappingsAnnotation(dexClass); |
| DexAnnotationAttribute dexAnnotationAttribute = annotation |
| .getAttributes().get(0); |
| if ("value".equals(dexAnnotationAttribute.getName())) { |
| DexEncodedValue encodedValue = dexAnnotationAttribute |
| .getEncodedValue(); |
| DexEncodedValue value = (DexEncodedValue) encodedValue |
| .getValue(); |
| List<DexAnnotationAttribute> defaults = |
| (List<DexAnnotationAttribute>) value.getValue(); |
| for (DexAnnotationAttribute defaultAttribute : defaults) { |
| mappings.put(defaultAttribute.getName(), |
| convertEncodedValue(defaultAttribute |
| .getEncodedValue())); |
| } |
| } |
| } |
| return mappings; |
| } |
| |
| |
| private void addAnnotationsToAnnotationFields(List<DexMethod> methods, |
| Set<SigAnnotationField> annotationFields) { |
| Map<String, SigAnnotationField> nameToAnnotationField = |
| new HashMap<String, SigAnnotationField>(); |
| |
| for (SigAnnotationField annotationField : annotationFields) { |
| nameToAnnotationField.put(annotationField.getName(), |
| annotationField); |
| } |
| |
| for (DexMethod method : methods) { |
| SigAnnotationField annotationField = nameToAnnotationField |
| .get(method.getName()); |
| annotationField.setAnnotations(convertAnnotations(method |
| .getAnnotations())); |
| } |
| } |
| |
| |
| private Set<SigAnnotationField> convertAnnotationFields( |
| List<DexMethod> list, Map<String, Object> mappings) { |
| Set<SigAnnotationField> annotationfields = |
| new HashSet<SigAnnotationField>(); |
| for (DexMethod dexMethod : list) { |
| if (isVisible(dexMethod, visibility)) { |
| annotationfields.add(convertAnnotationField(dexMethod, mappings |
| .get(dexMethod.getName()))); |
| } |
| } |
| return annotationfields; |
| } |
| |
| private SigAnnotationField convertAnnotationField(DexMethod dexMethod, |
| Object defaultValue) { |
| SigAnnotationField annotationField = new SigAnnotationField(dexMethod |
| .getName()); |
| annotationField.setDefaultValue(defaultValue); |
| annotationField.setModifiers(getModifier(dexMethod.getModifiers())); |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| annotationField.setType(parser.parseNonGenericType(dexMethod |
| .getReturnType())); |
| return annotationField; |
| } |
| |
| private IEnumConstant convertEnumConstant(DexField dexField) { |
| String qualifiedTypeName = getQualifiedName(dexField |
| .getDeclaringClass().getName()); |
| SigEnumConstant enumConstant = elementPool.getEnumConstant( |
| qualifiedTypeName, dexField.getName()); |
| Set<Modifier> modifiers = getModifier(dexField.getModifiers()); |
| modifiers.add(Modifier.STATIC); |
| enumConstant.setModifiers(modifiers); |
| |
| String typePackageName = getPackageName(dexField.getType()); |
| String typeName = getClassName(dexField.getType()); |
| enumConstant.setType(factory.getClassReference(typePackageName, |
| typeName)); |
| enumConstant.setAnnotations(convertAnnotations(dexField |
| .getAnnotations())); |
| return enumConstant; |
| } |
| |
| private Set<SigField> convertFields(List<DexField> dexFields) { |
| Set<SigField> fields = new HashSet<SigField>(); |
| for (DexField dexField : dexFields) { |
| if (isVisible(dexField, visibility)) { |
| fields.add(convertField(dexField)); |
| } |
| } |
| return fields; |
| } |
| |
| private SigField convertField(DexField dexField) { |
| String qualTypeName = getQualifiedName(dexField.getDeclaringClass() |
| .getName()); |
| SigField field = elementPool.getField(qualTypeName, dexField.getName()); |
| |
| field.setModifiers(getModifier(dexField.getModifiers())); |
| |
| field.setAnnotations(convertAnnotations(dexField.getAnnotations())); |
| |
| if (hasGenericSignature(dexField)) { |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| String declaringClassPackageName = getPackageName(dexField |
| .getDeclaringClass().getName()); |
| String declaringClassName = getClassName(dexField |
| .getDeclaringClass().getName()); |
| |
| parser.parseForField(factory.getClass(declaringClassPackageName, |
| declaringClassName), getGenericSignature(dexField)); |
| field.setType(parser.fieldType); |
| } else { |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| field.setType(parser.parseNonGenericType(dexField.getType())); |
| } |
| |
| return field; |
| } |
| |
| /** |
| * Converts a set of {@link DexMethod} to a set of corresponding |
| * {@link IConstructor}. This method ignores methods which are not |
| * constructors. |
| * |
| * @param methods |
| * the {@link DexMethod}s to convert |
| * @return the corresponding {@link IConstructor}s |
| */ |
| private Set<SigConstructor> convertConstructors(List<DexMethod> methods) { |
| Set<SigConstructor> constructors = new HashSet<SigConstructor>(); |
| for (DexMethod method : methods) { |
| if (isConstructor(method) && isVisible(method, visibility)) { |
| constructors.add(convertConstructor(method)); |
| } |
| } |
| return constructors; |
| } |
| |
| /** |
| * Converts a set of {@link DexMethod} to a set of corresponding |
| * {@link DexMethod}. This method ignores methods which are constructors. |
| * |
| * @param methods |
| * the {@link DexMethod}s to convert |
| * @return the corresponding {@link IConstructor}s |
| */ |
| private Set<SigMethod> convertMethods(List<DexMethod> methods) { |
| Set<SigMethod> sigMethods = new HashSet<SigMethod>(); |
| for (DexMethod method : methods) { |
| if (isMethod(method) && isVisible(method, visibility)) { |
| sigMethods.add(convertMethod(method)); |
| } |
| } |
| return sigMethods; |
| } |
| |
| /** |
| * Converts a dexMethod which must be a constructor to the corresponding |
| * {@link SigConstructor} instance. |
| * |
| * @param dexMethod |
| * the dex constructor to convert |
| * @return the corresponding {@link SigConstructor} |
| */ |
| public SigConstructor convertConstructor(DexMethod dexMethod) { |
| String declaringClassName = getClassName(dexMethod.getDeclaringClass() |
| .getName()); |
| |
| SigConstructor constructor = new SigConstructor(declaringClassName); |
| constructor.setModifiers(getModifier(dexMethod.getModifiers())); |
| String declaringClassPackageName = getPackageName(dexMethod |
| .getDeclaringClass().getName()); |
| |
| |
| SigClassDefinition declaringClass = factory.getClass( |
| declaringClassPackageName, declaringClassName); |
| constructor.setDeclaringClass(declaringClass); |
| |
| // Annotations |
| constructor.setAnnotations(convertAnnotations(dexMethod |
| .getAnnotations())); |
| |
| if (hasGenericSignature(dexMethod)) { |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| parser.parseForConstructor(constructor, |
| getGenericSignature(dexMethod)); |
| |
| // type parameters |
| constructor.setTypeParameters(parser.formalTypeParameters); |
| |
| // parameters |
| // generic parameter types parameters |
| List<DexParameter> dexParameters = dexMethod.getParameters(); |
| List<IParameter> parameters = new ArrayList<IParameter>( |
| parser.parameterTypes.size()); |
| Iterator<DexParameter> iterator = dexParameters.iterator(); |
| for (ITypeReference parameterType : parser.parameterTypes) { |
| SigParameter parameter = new SigParameter(parameterType); |
| iterator.hasNext(); |
| DexParameter dexParam = iterator.next(); |
| parameter.setAnnotations(convertAnnotations(dexParam |
| .getAnnotations())); |
| parameters.add(parameter); |
| } |
| |
| constructor.setParameters(parameters); |
| |
| // exceptions |
| constructor.setExceptions(new HashSet<ITypeReference>( |
| parser.exceptionTypes)); |
| |
| } else { |
| convertNonGenericExecutableMember(constructor, dexMethod); |
| |
| // remove first parameter of non static inner class constructors |
| // implicit outer.this reference |
| if (declaringClass.getDeclaringClass() != null) { |
| if (!declaringClass.getModifiers().contains(Modifier.STATIC)) { |
| if (constructor.getParameters().isEmpty()) { |
| throw new IllegalStateException( |
| "Expected at least one parameter!"); |
| } |
| IParameter first = constructor.getParameters().remove(0); |
| String enclosingName = declaringClass.getDeclaringClass() |
| .getName(); |
| String firstParameterTypeName = ((IClassReference) first |
| .getType()).getClassDefinition().getName(); |
| if (!enclosingName.equals(firstParameterTypeName)) |
| throw new IllegalStateException( |
| "Expected first constructor parameter of type " |
| + enclosingName); |
| } |
| } |
| } |
| |
| addExceptions(constructor, dexMethod); |
| return constructor; |
| } |
| |
| public SigMethod convertMethod(DexMethod dexMethod) { |
| SigMethod method = new SigMethod(dexMethod.getName()); |
| method.setModifiers(getModifier(dexMethod.getModifiers())); |
| |
| String declaringClassPackageName = getPackageName(dexMethod |
| .getDeclaringClass().getName()); |
| String declaringClassName = getClassName(dexMethod.getDeclaringClass() |
| .getName()); |
| |
| method.setDeclaringClass(factory.getClass(declaringClassPackageName, |
| declaringClassName)); |
| |
| // Annotations |
| method.setAnnotations(convertAnnotations(dexMethod.getAnnotations())); |
| |
| if (hasGenericSignature(dexMethod)) { |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| parser.parseForMethod(method, getGenericSignature(dexMethod)); |
| |
| // type parameters |
| method.setTypeParameters(parser.formalTypeParameters); |
| |
| // generic parameter types parameters |
| List<DexParameter> dexParameters = dexMethod.getParameters(); |
| List<IParameter> parameters = new ArrayList<IParameter>( |
| parser.parameterTypes.size()); |
| Iterator<DexParameter> iterator = dexParameters.iterator(); |
| for (ITypeReference parameterType : parser.parameterTypes) { |
| SigParameter parameter = new SigParameter(parameterType); |
| iterator.hasNext(); |
| DexParameter dexParam = iterator.next(); |
| parameter.setAnnotations(convertAnnotations(dexParam |
| .getAnnotations())); |
| parameters.add(parameter); |
| } |
| method.setParameters(parameters); |
| |
| // exceptions |
| method.setExceptions(new HashSet<ITypeReference>( |
| parser.exceptionTypes)); |
| method.setReturnType(parser.returnType); |
| |
| } else { |
| convertNonGenericExecutableMember(method, dexMethod); |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| ITypeReference type = parser.parseNonGenericReturnType(dexMethod |
| .getReturnType()); |
| method.setReturnType(type); |
| } |
| addExceptions(method, dexMethod); |
| return method; |
| } |
| |
| private void addExceptions(SigExecutableMember member, |
| DexMethod dexMethod) { |
| if (declaresExceptions(dexMethod)) { |
| String exceptionSignature = getExceptionSignature(dexMethod); |
| Set<String> exceptionTypeNames = splitTypeList(exceptionSignature); |
| Set<ITypeReference> exceptions = new HashSet<ITypeReference>(); |
| |
| for (String exTypeName : exceptionTypeNames) { |
| String packageName = getPackageName(exTypeName); |
| String className = getClassName(exTypeName); |
| exceptions.add(factory |
| .getClassReference(packageName, className)); |
| } |
| member.setExceptions(exceptions); |
| } else { |
| member.setExceptions(EMPTY_EXCEPTIONS); |
| } |
| } |
| |
| private void convertNonGenericExecutableMember(SigExecutableMember member, |
| DexMethod dexMethod) { |
| List<DexParameter> dexParameters = dexMethod.getParameters(); |
| List<IParameter> parameters = new ArrayList<IParameter>(dexParameters |
| .size()); |
| |
| for (DexParameter dexParameter : dexParameters) { |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| ITypeReference type = parser.parseNonGenericType(dexParameter |
| .getTypeName()); |
| SigParameter parameter = new SigParameter(type); |
| parameters.add(parameter); |
| // Annotations |
| parameter.setAnnotations(convertAnnotations(dexParameter |
| .getAnnotations())); |
| } |
| member.setParameters(parameters); |
| |
| member.setTypeParameters(EMPTY_TYPE_VARIABLES); |
| |
| // if (declaresExceptions(dexMethod)) { |
| // String exceptionSignature = getExceptionSignature(dexMethod); |
| // Set<String> exceptionTypeNames = splitTypeList(exceptionSignature); |
| // Set<IType> exceptions = new HashSet<IType>(); |
| // |
| // for (String exTypeName : exceptionTypeNames) { |
| // String packageName = getPackageName(exTypeName); |
| // String className = getClassName(exTypeName); |
| // exceptions.add(factory.getClass(packageName, className)); |
| // } |
| // member.setExceptions(exceptions); |
| // } else { |
| // member.setExceptions(EMPTY_EXCEPTIONS); |
| // } |
| } |
| |
| /** |
| * Converts a set of {@link DexAnnotation} to a set of corresponding |
| * {@link SigAnnotation}. |
| * |
| * @param dexAnnotations |
| * the {@link DexAnnotation}s to convert |
| * @return the corresponding {@link SigAnnotation}s |
| */ |
| private Set<IAnnotation> convertAnnotations( |
| Set<DexAnnotation> dexAnnotations) { |
| Set<IAnnotation> annotations = new HashSet<IAnnotation>(); |
| for (DexAnnotation dexAnnotation : dexAnnotations) { |
| if (!isInternalAnnotation(dexAnnotation)) { |
| annotations.add(convertAnnotation(dexAnnotation)); |
| } |
| } |
| return annotations; |
| } |
| |
| /** |
| * Converts a {@link DexAnnotation} to the corresponding |
| * {@link SigAnnotation}. |
| * |
| * @param dexAnnotation |
| * the {@link DexAnnotation} to convert |
| * @return the corresponding {@link SigAnnotation} |
| */ |
| protected SigAnnotation convertAnnotation(DexAnnotation dexAnnotation) { |
| SigAnnotation sigAnnotation = new SigAnnotation(); |
| String packageName = getPackageName(dexAnnotation.getTypeName()); |
| String className = getClassName(dexAnnotation.getTypeName()); |
| sigAnnotation |
| .setType(factory.getClassReference(packageName, className)); |
| sigAnnotation.setElements(convertAnnotationElements(dexAnnotation |
| .getAttributes())); |
| return sigAnnotation; |
| } |
| |
| private Set<IAnnotationElement> convertAnnotationElements( |
| List<DexAnnotationAttribute> attributes) { |
| Set<IAnnotationElement> annotationAttributes = |
| new HashSet<IAnnotationElement>(); |
| for (DexAnnotationAttribute dexAnnotationAttribute : attributes) { |
| annotationAttributes |
| .add(convertAnnotationAttribute(dexAnnotationAttribute)); |
| } |
| return annotationAttributes; |
| } |
| |
| private IAnnotationElement convertAnnotationAttribute( |
| DexAnnotationAttribute dexAnnotationAttribute) { |
| |
| SigAnnotationElement sigElement = new SigAnnotationElement(); |
| String nameOfField = dexAnnotationAttribute.getName(); |
| |
| |
| String typeName = dexAnnotationAttribute.getAnnotation().getTypeName(); |
| SigClassDefinition annotationClass = factory.getClass( |
| getPackageName(typeName), getClassName(typeName)); |
| if (!Uninitialized.isInitialized( |
| annotationClass.getAnnotationFields())) { |
| initializeClass(getPackageName(typeName), getClassName(typeName)); |
| } |
| for (IAnnotationField field : annotationClass.getAnnotationFields()) { |
| if (nameOfField.equals(field.getName())) { |
| sigElement.setDeclaringField(field); |
| } |
| } |
| |
| sigElement.setValue(convertEncodedValue(dexAnnotationAttribute |
| .getEncodedValue())); |
| return sigElement; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Object convertEncodedValue(DexEncodedValue dexEnodedValue) { |
| Object value = null; |
| switch (dexEnodedValue.getType()) { |
| case VALUE_INT: |
| case VALUE_BOOLEAN: |
| case VALUE_BYTE: |
| case VALUE_CHAR: |
| case VALUE_DOUBLE: |
| case VALUE_FLOAT: |
| case VALUE_LONG: |
| case VALUE_NULL: |
| case VALUE_STRING: |
| case VALUE_SHORT: |
| value = dexEnodedValue.getValue(); |
| break; |
| case VALUE_ARRAY: { |
| List<DexEncodedValue> dexValues = |
| (List<DexEncodedValue>) dexEnodedValue.getValue(); |
| Object[] arrayValues = new Object[dexValues.size()]; |
| int i = 0; |
| for (DexEncodedValue dexValue : dexValues) { |
| arrayValues[i++] = convertEncodedValue(dexValue); |
| } |
| value = arrayValues; |
| break; |
| } |
| case VALUE_ANNOTATION: { |
| DexEncodedAnnotation annotation = |
| (DexEncodedAnnotation) dexEnodedValue.getValue(); |
| SigAnnotation sigAnnotation = new SigAnnotation(); |
| String packageName = getPackageName(annotation.getTypeName()); |
| String className = getClassName(annotation.getTypeName()); |
| sigAnnotation.setType(factory.getClassReference(packageName, |
| className)); |
| |
| sigAnnotation.setElements(convertAnnotationElements(annotation |
| .getValue())); |
| value = sigAnnotation; |
| break; |
| } |
| case VALUE_FIELD: { |
| String fieldDesc = (String) dexEnodedValue.getValue(); |
| // FORMAT La/b/E;!CONSTANT |
| String[] typeAndFieldName = fieldDesc.split("!"); |
| String typeName = typeAndFieldName[0]; |
| String fieldName = typeAndFieldName[1]; |
| value = elementPool.getField(getQualifiedName(typeName), fieldName); |
| break; |
| } |
| case VALUE_ENUM: { |
| String fieldDesc = (String) dexEnodedValue.getValue(); |
| // FORMAT La/b/E;!CONSTANT |
| String[] typeAndFieldName = fieldDesc.split("!"); |
| String typeName = typeAndFieldName[0]; |
| String fieldName = typeAndFieldName[1]; |
| value = elementPool.getEnumConstant(getQualifiedName(typeName), |
| fieldName); |
| break; |
| } |
| case VALUE_TYPE: { |
| String typeName = (String) dexEnodedValue.getValue(); |
| GenericSignatureParser parser = new GenericSignatureParser(factory, |
| this); |
| value = parser.parseNonGenericReturnType(typeName); |
| break; |
| } |
| default: |
| throw new IllegalStateException(); |
| } |
| return value; |
| } |
| |
| public IClassDefinition initializeClass(String packageName, |
| String className) { |
| String dexName = getDexName(packageName, className); |
| DexClass dexClass = dexNameToDexClass.get(dexName); |
| return convertClass(dexClass); |
| } |
| } |