2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.dx.dex.cf;
19 import com.android.dx.cf.attrib.AttAnnotationDefault;
20 import com.android.dx.cf.attrib.AttEnclosingMethod;
21 import com.android.dx.cf.attrib.AttExceptions;
22 import com.android.dx.cf.attrib.AttInnerClasses;
23 import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
24 import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
25 import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
26 import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
27 import com.android.dx.cf.attrib.AttSignature;
28 import com.android.dx.cf.attrib.InnerClassList;
29 import com.android.dx.cf.direct.DirectClassFile;
30 import com.android.dx.cf.iface.AttributeList;
31 import com.android.dx.cf.iface.Method;
32 import com.android.dx.cf.iface.MethodList;
33 import com.android.dx.dex.file.AnnotationUtils;
34 import com.android.dx.rop.annotation.Annotation;
35 import com.android.dx.rop.annotation.AnnotationVisibility;
36 import com.android.dx.rop.annotation.Annotations;
37 import com.android.dx.rop.annotation.AnnotationsList;
38 import com.android.dx.rop.annotation.NameValuePair;
39 import com.android.dx.rop.code.AccessFlags;
40 import com.android.dx.rop.cst.CstMethodRef;
41 import com.android.dx.rop.cst.CstNat;
42 import com.android.dx.rop.cst.CstType;
43 import com.android.dx.rop.type.StdTypeList;
44 import com.android.dx.rop.type.Type;
45 import com.android.dx.rop.type.TypeList;
46 import com.android.dx.util.Warning;
48 import java.util.ArrayList;
51 * Utility methods that translate various classfile attributes
52 * into forms suitable for use in creating {@code dex} files.
54 /*package*/ class AttributeTranslator {
56 * This class is uninstantiable.
58 private AttributeTranslator() {
59 // This space intentionally left blank.
63 * Gets the list of thrown exceptions for a given method.
65 * @param method {@code non-null;} the method in question
66 * @return {@code non-null;} the list of thrown exceptions
68 public static TypeList getExceptions(Method method) {
69 AttributeList attribs = method.getAttributes();
70 AttExceptions exceptions = (AttExceptions)
71 attribs.findFirst(AttExceptions.ATTRIBUTE_NAME);
73 if (exceptions == null) {
74 return StdTypeList.EMPTY;
77 return exceptions.getExceptions();
81 * Gets the annotations out of a given {@link AttributeList}. This
82 * combines both visible and invisible annotations into a single
83 * result set and also adds in a system annotation for the
84 * {@code Signature} attribute if present.
86 * @param attribs {@code non-null;} the attributes list to search in
87 * @return {@code non-null;} the set of annotations, which may be empty
89 public static Annotations getAnnotations(AttributeList attribs) {
90 Annotations result = getAnnotations0(attribs);
91 Annotation signature = getSignature(attribs);
93 if (signature != null) {
94 result = Annotations.combine(result, signature);
101 * Gets the annotations out of a given class, similar to {@link
102 * #getAnnotations}, also including annotations for translations
103 * of class-level attributes {@code EnclosingMethod} and
104 * {@code InnerClasses}, if present. Additionally, if the
105 * class is an annotation class, then this also includes a
106 * representation of all the {@code AnnotationDefault}
109 * @param cf {@code non-null;} the class in question
110 * @param args {@code non-null;} the high-level options
111 * @return {@code non-null;} the set of annotations, which may be empty
113 public static Annotations getClassAnnotations(DirectClassFile cf,
115 CstType thisClass = cf.getThisClass();
116 AttributeList attribs = cf.getAttributes();
117 Annotations result = getAnnotations(attribs);
118 Annotation enclosingMethod = translateEnclosingMethod(attribs);
121 Annotations innerClassAnnotations =
122 translateInnerClasses(thisClass, attribs,
123 enclosingMethod == null);
124 if (innerClassAnnotations != null) {
125 result = Annotations.combine(result, innerClassAnnotations);
127 } catch (Warning warn) {
128 args.warn.println("warning: " + warn.getMessage());
131 if (enclosingMethod != null) {
132 result = Annotations.combine(result, enclosingMethod);
135 if (AccessFlags.isAnnotation(cf.getAccessFlags())) {
136 Annotation annotationDefault =
137 translateAnnotationDefaults(cf);
138 if (annotationDefault != null) {
139 result = Annotations.combine(result, annotationDefault);
147 * Gets the annotations out of a given method, similar to {@link
148 * #getAnnotations}, also including an annotation for the translation
149 * of the method-specific attribute {@code Exceptions}.
151 * @param method {@code non-null;} the method in question
152 * @return {@code non-null;} the set of annotations, which may be empty
154 public static Annotations getMethodAnnotations(Method method) {
155 Annotations result = getAnnotations(method.getAttributes());
156 TypeList exceptions = getExceptions(method);
158 if (exceptions.size() != 0) {
159 Annotation throwsAnnotation =
160 AnnotationUtils.makeThrows(exceptions);
161 result = Annotations.combine(result, throwsAnnotation);
168 * Helper method for {@link #getAnnotations} which just gets the
169 * existing annotations, per se.
171 * @param attribs {@code non-null;} the attributes list to search in
172 * @return {@code non-null;} the set of annotations, which may be empty
174 private static Annotations getAnnotations0(AttributeList attribs) {
175 AttRuntimeVisibleAnnotations visible =
176 (AttRuntimeVisibleAnnotations)
177 attribs.findFirst(AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME);
178 AttRuntimeInvisibleAnnotations invisible =
179 (AttRuntimeInvisibleAnnotations)
180 attribs.findFirst(AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME);
182 if (visible == null) {
183 if (invisible == null) {
184 return Annotations.EMPTY;
186 return invisible.getAnnotations();
189 if (invisible == null) {
190 return visible.getAnnotations();
193 // Both are non-null, so combine them.
195 return Annotations.combine(visible.getAnnotations(),
196 invisible.getAnnotations());
200 * Gets the {@code Signature} attribute out of a given
201 * {@link AttributeList}, if any, translating it to an annotation.
203 * @param attribs {@code non-null;} the attributes list to search in
204 * @return {@code null-ok;} the converted {@code Signature} annotation,
205 * if there was an attribute to translate
207 private static Annotation getSignature(AttributeList attribs) {
208 AttSignature signature = (AttSignature)
209 attribs.findFirst(AttSignature.ATTRIBUTE_NAME);
211 if (signature == null) {
215 return AnnotationUtils.makeSignature(signature.getSignature());
219 * Gets the {@code EnclosingMethod} attribute out of a given
220 * {@link AttributeList}, if any, translating it to an annotation.
221 * If the class really has an enclosing method, this returns an
222 * {@code EnclosingMethod} annotation; if not, this returns
223 * an {@code EnclosingClass} annotation.
225 * @param attribs {@code non-null;} the attributes list to search in
226 * @return {@code null-ok;} the converted {@code EnclosingMethod} or
227 * {@code EnclosingClass} annotation, if there was an
228 * attribute to translate
230 private static Annotation translateEnclosingMethod(AttributeList attribs) {
231 AttEnclosingMethod enclosingMethod = (AttEnclosingMethod)
232 attribs.findFirst(AttEnclosingMethod.ATTRIBUTE_NAME);
234 if (enclosingMethod == null) {
238 CstType enclosingClass = enclosingMethod.getEnclosingClass();
239 CstNat nat = enclosingMethod.getMethod();
243 * Dalvik doesn't use EnclosingMethod annotations unless
244 * there really is an enclosing method. Anonymous classes
245 * are unambiguously identified by having an InnerClass
246 * annotation with an empty name along with an appropriate
249 return AnnotationUtils.makeEnclosingClass(enclosingClass);
252 return AnnotationUtils.makeEnclosingMethod(
253 new CstMethodRef(enclosingClass, nat));
257 * Gets the {@code InnerClasses} attribute out of a given
258 * {@link AttributeList}, if any, translating it to one or more of an
259 * {@code InnerClass}, {@code EnclosingClass}, or
260 * {@code MemberClasses} annotation.
262 * @param thisClass {@code non-null;} type representing the class being
264 * @param attribs {@code non-null;} the attributes list to search in
265 * @param needEnclosingClass whether to include an
266 * {@code EnclosingClass} annotation
267 * @return {@code null-ok;} the converted list of annotations, if there
268 * was an attribute to translate
270 private static Annotations translateInnerClasses(CstType thisClass,
271 AttributeList attribs, boolean needEnclosingClass) {
272 AttInnerClasses innerClasses = (AttInnerClasses)
273 attribs.findFirst(AttInnerClasses.ATTRIBUTE_NAME);
275 if (innerClasses == null) {
280 * Search the list for the element representing the current class
281 * as well as for any named member classes.
284 InnerClassList list = innerClasses.getInnerClasses();
285 int size = list.size();
286 InnerClassList.Item foundThisClass = null;
287 ArrayList<Type> membersList = new ArrayList<Type>();
289 for (int i = 0; i < size; i++) {
290 InnerClassList.Item item = list.get(i);
291 CstType innerClass = item.getInnerClass();
292 if (innerClass.equals(thisClass)) {
293 foundThisClass = item;
294 } else if (thisClass.equals(item.getOuterClass())) {
295 membersList.add(innerClass.getClassType());
299 int membersSize = membersList.size();
301 if ((foundThisClass == null) && (membersSize == 0)) {
305 Annotations result = new Annotations();
307 if (foundThisClass != null) {
308 result.add(AnnotationUtils.makeInnerClass(
309 foundThisClass.getInnerName(),
310 foundThisClass.getAccessFlags()));
311 if (needEnclosingClass) {
312 CstType outer = foundThisClass.getOuterClass();
315 "Ignoring InnerClasses attribute for an " +
316 "anonymous inner class\n" +
317 "(" + thisClass.toHuman() +
318 ") that doesn't come with an\n" +
319 "associated EnclosingMethod attribute. " +
320 "This class was probably produced by a\n" +
321 "compiler that did not target the modern " +
322 ".class file format. The recommended\n" +
323 "solution is to recompile the class from " +
324 "source, using an up-to-date compiler\n" +
325 "and without specifying any \"-target\" type " +
326 "options. The consequence of ignoring\n" +
327 "this warning is that reflective operations " +
328 "on this class will incorrectly\n" +
329 "indicate that it is *not* an inner class.");
331 result.add(AnnotationUtils.makeEnclosingClass(
332 foundThisClass.getOuterClass()));
336 if (membersSize != 0) {
337 StdTypeList typeList = new StdTypeList(membersSize);
338 for (int i = 0; i < membersSize; i++) {
339 typeList.set(i, membersList.get(i));
341 typeList.setImmutable();
342 result.add(AnnotationUtils.makeMemberClasses(typeList));
345 result.setImmutable();
350 * Gets the parameter annotations out of a given method. This
351 * combines both visible and invisible annotations into a single
354 * @param method {@code non-null;} the method in question
355 * @return {@code non-null;} the list of annotation sets, which may be
358 public static AnnotationsList getParameterAnnotations(Method method) {
359 AttributeList attribs = method.getAttributes();
360 AttRuntimeVisibleParameterAnnotations visible =
361 (AttRuntimeVisibleParameterAnnotations)
363 AttRuntimeVisibleParameterAnnotations.ATTRIBUTE_NAME);
364 AttRuntimeInvisibleParameterAnnotations invisible =
365 (AttRuntimeInvisibleParameterAnnotations)
367 AttRuntimeInvisibleParameterAnnotations.ATTRIBUTE_NAME);
369 if (visible == null) {
370 if (invisible == null) {
371 return AnnotationsList.EMPTY;
373 return invisible.getParameterAnnotations();
376 if (invisible == null) {
377 return visible.getParameterAnnotations();
380 // Both are non-null, so combine them.
382 return AnnotationsList.combine(visible.getParameterAnnotations(),
383 invisible.getParameterAnnotations());
387 * Gets the {@code AnnotationDefault} attributes out of a
388 * given class, if any, reforming them as an
389 * {@code AnnotationDefault} annotation.
391 * @param cf {@code non-null;} the class in question
392 * @return {@code null-ok;} an appropriately-constructed
393 * {@code AnnotationDefault} annotation, if there were any
394 * annotation defaults in the class, or {@code null} if not
396 private static Annotation translateAnnotationDefaults(DirectClassFile cf) {
397 CstType thisClass = cf.getThisClass();
398 MethodList methods = cf.getMethods();
399 int sz = methods.size();
401 new Annotation(thisClass, AnnotationVisibility.EMBEDDED);
404 for (int i = 0; i < sz; i++) {
405 Method one = methods.get(i);
406 AttributeList attribs = one.getAttributes();
407 AttAnnotationDefault oneDefault = (AttAnnotationDefault)
408 attribs.findFirst(AttAnnotationDefault.ATTRIBUTE_NAME);
410 if (oneDefault != null) {
411 NameValuePair pair = new NameValuePair(
412 one.getNat().getName(),
413 oneDefault.getValue());
423 result.setImmutable();
424 return AnnotationUtils.makeAnnotationDefault(result);