/* * Copyright (C) 2007 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 com.android.dx.dex.cf; import com.android.dx.cf.attrib.AttAnnotationDefault; import com.android.dx.cf.attrib.AttEnclosingMethod; import com.android.dx.cf.attrib.AttExceptions; import com.android.dx.cf.attrib.AttInnerClasses; import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations; import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations; import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations; import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations; import com.android.dx.cf.attrib.AttSignature; import com.android.dx.cf.attrib.InnerClassList; import com.android.dx.cf.direct.DirectClassFile; import com.android.dx.cf.direct.StdAttributeFactory; import com.android.dx.cf.iface.AttributeList; import com.android.dx.cf.iface.Method; import com.android.dx.cf.iface.MethodList; import com.android.dx.dex.file.AnnotationUtils; import com.android.dx.rop.annotation.Annotation; import com.android.dx.rop.annotation.AnnotationVisibility; import com.android.dx.rop.annotation.Annotations; import com.android.dx.rop.annotation.AnnotationsList; import com.android.dx.rop.annotation.NameValuePair; import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.cst.CstMethodRef; import com.android.dx.rop.cst.CstNat; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.cst.CstUtf8; import com.android.dx.rop.type.StdTypeList; import com.android.dx.rop.type.Type; import com.android.dx.rop.type.TypeList; import com.android.dx.util.Warning; import java.util.ArrayList; /** * Utility methods that translate various classfile attributes * into forms suitable for use in creating {@code dex} files. */ /*package*/ class AttributeTranslator { /** * This class is uninstantiable. */ private AttributeTranslator() { // This space intentionally left blank. } /** * Gets the list of thrown exceptions for a given method. * * @param method {@code non-null;} the method in question * @return {@code non-null;} the list of thrown exceptions */ public static TypeList getExceptions(Method method) { AttributeList attribs = method.getAttributes(); AttExceptions exceptions = (AttExceptions) attribs.findFirst(AttExceptions.ATTRIBUTE_NAME); if (exceptions == null) { return StdTypeList.EMPTY; } return exceptions.getExceptions(); } /** * Gets the annotations out of a given {@link AttributeList}. This * combines both visible and invisible annotations into a single * result set and also adds in a system annotation for the * {@code Signature} attribute if present. * * @param attribs {@code non-null;} the attributes list to search in * @return {@code non-null;} the set of annotations, which may be empty */ public static Annotations getAnnotations(AttributeList attribs) { Annotations result = getAnnotations0(attribs); Annotation signature = getSignature(attribs); if (signature != null) { result = Annotations.combine(result, signature); } return result; } /** * Gets the annotations out of a given class, similar to {@link * #getAnnotations}, also including annotations for translations * of class-level attributes {@code EnclosingMethod} and * {@code InnerClasses}, if present. Additionally, if the * class is an annotation class, then this also includes a * representation of all the {@code AnnotationDefault} * values. * * @param cf {@code non-null;} the class in question * @param args {@code non-null;} the high-level options * @return {@code non-null;} the set of annotations, which may be empty */ public static Annotations getClassAnnotations(DirectClassFile cf, CfOptions args) { CstType thisClass = cf.getThisClass(); AttributeList attribs = cf.getAttributes(); Annotations result = getAnnotations(attribs); Annotation enclosingMethod = translateEnclosingMethod(attribs); try { Annotations innerClassAnnotations = translateInnerClasses(thisClass, attribs, enclosingMethod == null); if (innerClassAnnotations != null) { result = Annotations.combine(result, innerClassAnnotations); } } catch (Warning warn) { args.warn.println("warning: " + warn.getMessage()); } if (enclosingMethod != null) { result = Annotations.combine(result, enclosingMethod); } if (AccessFlags.isAnnotation(cf.getAccessFlags())) { Annotation annotationDefault = translateAnnotationDefaults(cf); if (annotationDefault != null) { result = Annotations.combine(result, annotationDefault); } } return result; } /** * Gets the annotations out of a given method, similar to {@link * #getAnnotations}, also including an annotation for the translation * of the method-specific attribute {@code Exceptions}. * * @param method {@code non-null;} the method in question * @return {@code non-null;} the set of annotations, which may be empty */ public static Annotations getMethodAnnotations(Method method) { Annotations result = getAnnotations(method.getAttributes()); TypeList exceptions = getExceptions(method); if (exceptions.size() != 0) { Annotation throwsAnnotation = AnnotationUtils.makeThrows(exceptions); result = Annotations.combine(result, throwsAnnotation); } return result; } /** * Helper method for {@link #getAnnotations} which just gets the * existing annotations, per se. * * @param attribs {@code non-null;} the attributes list to search in * @return {@code non-null;} the set of annotations, which may be empty */ private static Annotations getAnnotations0(AttributeList attribs) { AttRuntimeVisibleAnnotations visible = (AttRuntimeVisibleAnnotations) attribs.findFirst(AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME); AttRuntimeInvisibleAnnotations invisible = (AttRuntimeInvisibleAnnotations) attribs.findFirst(AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME); if (visible == null) { if (invisible == null) { return Annotations.EMPTY; } return invisible.getAnnotations(); } if (invisible == null) { return visible.getAnnotations(); } // Both are non-null, so combine them. return Annotations.combine(visible.getAnnotations(), invisible.getAnnotations()); } /** * Gets the {@code Signature} attribute out of a given * {@link AttributeList}, if any, translating it to an annotation. * * @param attribs {@code non-null;} the attributes list to search in * @return {@code null-ok;} the converted {@code Signature} annotation, * if there was an attribute to translate */ private static Annotation getSignature(AttributeList attribs) { AttSignature signature = (AttSignature) attribs.findFirst(AttSignature.ATTRIBUTE_NAME); if (signature == null) { return null; } return AnnotationUtils.makeSignature(signature.getSignature()); } /** * Gets the {@code EnclosingMethod} attribute out of a given * {@link AttributeList}, if any, translating it to an annotation. * If the class really has an enclosing method, this returns an * {@code EnclosingMethod} annotation; if not, this returns * an {@code EnclosingClass} annotation. * * @param attribs {@code non-null;} the attributes list to search in * @return {@code null-ok;} the converted {@code EnclosingMethod} or * {@code EnclosingClass} annotation, if there was an * attribute to translate */ private static Annotation translateEnclosingMethod(AttributeList attribs) { AttEnclosingMethod enclosingMethod = (AttEnclosingMethod) attribs.findFirst(AttEnclosingMethod.ATTRIBUTE_NAME); if (enclosingMethod == null) { return null; } CstType enclosingClass = enclosingMethod.getEnclosingClass(); CstNat nat = enclosingMethod.getMethod(); if (nat == null) { /* * Dalvik doesn't use EnclosingMethod annotations unless * there really is an enclosing method. Anonymous classes * are unambiguously identified by having an InnerClass * annotation with an empty name along with an appropriate * EnclosingClass. */ return AnnotationUtils.makeEnclosingClass(enclosingClass); } return AnnotationUtils.makeEnclosingMethod( new CstMethodRef(enclosingClass, nat)); } /** * Gets the {@code InnerClasses} attribute out of a given * {@link AttributeList}, if any, translating it to one or more of an * {@code InnerClass}, {@code EnclosingClass}, or * {@code MemberClasses} annotation. * * @param thisClass {@code non-null;} type representing the class being processed * @param attribs {@code non-null;} the attributes list to search in * @param needEnclosingClass whether to include an * {@code EnclosingClass} annotation * @return {@code null-ok;} the converted list of annotations, if there * was an attribute to translate */ private static Annotations translateInnerClasses(CstType thisClass, AttributeList attribs, boolean needEnclosingClass) { AttInnerClasses innerClasses = (AttInnerClasses) attribs.findFirst(AttInnerClasses.ATTRIBUTE_NAME); if (innerClasses == null) { return null; } /* * Search the list for the element representing the current class * as well as for any named member classes. */ InnerClassList list = innerClasses.getInnerClasses(); int size = list.size(); InnerClassList.Item foundThisClass = null; ArrayList<Type> membersList = new ArrayList<Type>(); for (int i = 0; i < size; i++) { InnerClassList.Item item = list.get(i); CstType innerClass = item.getInnerClass(); if (innerClass.equals(thisClass)) { foundThisClass = item; } else if (thisClass.equals(item.getOuterClass())) { membersList.add(innerClass.getClassType()); } } int membersSize = membersList.size(); if ((foundThisClass == null) && (membersSize == 0)) { return null; } Annotations result = new Annotations(); if (foundThisClass != null) { result.add(AnnotationUtils.makeInnerClass( foundThisClass.getInnerName(), foundThisClass.getAccessFlags())); if (needEnclosingClass) { CstType outer = foundThisClass.getOuterClass(); if (outer == null) { throw new Warning( "Ignoring InnerClasses attribute for an " + "anonymous inner class that doesn't come with " + "an associated EnclosingMethod attribute. " + "(This class was probably produced by a broken " + "compiler.)"); } result.add(AnnotationUtils.makeEnclosingClass( foundThisClass.getOuterClass())); } } if (membersSize != 0) { StdTypeList typeList = new StdTypeList(membersSize); for (int i = 0; i < membersSize; i++) { typeList.set(i, membersList.get(i)); } typeList.setImmutable(); result.add(AnnotationUtils.makeMemberClasses(typeList)); } result.setImmutable(); return result; } /** * Gets the parameter annotations out of a given method. This * combines both visible and invisible annotations into a single * result set. * * @param method {@code non-null;} the method in question * @return {@code non-null;} the list of annotation sets, which may be empty */ public static AnnotationsList getParameterAnnotations(Method method) { AttributeList attribs = method.getAttributes(); AttRuntimeVisibleParameterAnnotations visible = (AttRuntimeVisibleParameterAnnotations) attribs.findFirst( AttRuntimeVisibleParameterAnnotations.ATTRIBUTE_NAME); AttRuntimeInvisibleParameterAnnotations invisible = (AttRuntimeInvisibleParameterAnnotations) attribs.findFirst( AttRuntimeInvisibleParameterAnnotations.ATTRIBUTE_NAME); if (visible == null) { if (invisible == null) { return AnnotationsList.EMPTY; } return invisible.getParameterAnnotations(); } if (invisible == null) { return visible.getParameterAnnotations(); } // Both are non-null, so combine them. return AnnotationsList.combine(visible.getParameterAnnotations(), invisible.getParameterAnnotations()); } /** * Gets the {@code AnnotationDefault} attributes out of a * given class, if any, reforming them as an * {@code AnnotationDefault} annotation. * * @param cf {@code non-null;} the class in question * @return {@code null-ok;} an appropriately-constructed * {@code AnnotationDefault} annotation, if there were any * annotation defaults in the class, or {@code null} if not */ private static Annotation translateAnnotationDefaults(DirectClassFile cf) { CstType thisClass = cf.getThisClass(); MethodList methods = cf.getMethods(); int sz = methods.size(); Annotation result = new Annotation(thisClass, AnnotationVisibility.EMBEDDED); boolean any = false; for (int i = 0; i < sz; i++) { Method one = methods.get(i); AttributeList attribs = one.getAttributes(); AttAnnotationDefault oneDefault = (AttAnnotationDefault) attribs.findFirst(AttAnnotationDefault.ATTRIBUTE_NAME); if (oneDefault != null) { NameValuePair pair = new NameValuePair( one.getNat().getName(), oneDefault.getValue()); result.add(pair); any = true; } } if (! any) { return null; } result.setImmutable(); return AnnotationUtils.makeAnnotationDefault(result); } }