/*
* 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</code> 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 non-null; the method in question
* @return 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</code> attribute if present.
*
* @param attribs non-null; the attributes list to search in
* @return 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</code> and
* <code>InnerClasses</code>, if present. Additionally, if the
* class is an annotation class, then this also includes a
* representation of all the <code>AnnotationDefault</code>
* values.
*
* @param cf non-null; the class in question
* @param args non-null; the high-level options
* @return 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</code>.
*
* @param method non-null; the method in question
* @return 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 non-null; the attributes list to search in
* @return 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</code> attribute out of a given
* {@link AttributeList}, if any, translating it to an annotation.
*
* @param attribs non-null; the attributes list to search in
* @return null-ok; the converted <code>Signature</code> 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</code> 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</code> annotation; if not, this returns
* an <code>EnclosingClass</code> annotation.
*
* @param attribs non-null; the attributes list to search in
* @return null-ok; the converted <code>EnclosingMethod</code> or
* <code>EnclosingClass</code> 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</code> attribute out of a given
* {@link AttributeList}, if any, translating it to one or more of an
* <code>InnerClass</code>, <code>EnclosingClass</code>, or
* <code>MemberClasses</code> annotation.
*
* @param thisClass non-null; type representing the class being processed
* @param attribs non-null; the attributes list to search in
* @param needEnclosingClass whether to include an
* <code>EnclosingClass</code> annotation
* @return 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 non-null; the method in question
* @return 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</code> attributes out of a
* given class, if any, reforming them as an
* <code>AnnotationDefault</code> annotation.
*
* @param cf non-null; the class in question
* @return null-ok; an appropriately-constructed
* <code>AnnotationDefault</code> annotation, if there were any
* annotation defaults in the class, or <code>null<code> 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);
}
}
|