OSDN Git Service

8dc8b928d25acd14b15a515b21e17b7b0cd12785
[android-x86/dalvik.git] / dx / src / com / android / dx / dex / cf / AttributeTranslator.java
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.dx.dex.cf;
18
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;
47
48 import java.util.ArrayList;
49
50 /**
51  * Utility methods that translate various classfile attributes
52  * into forms suitable for use in creating {@code dex} files.
53  */
54 /*package*/ class AttributeTranslator {
55     /**
56      * This class is uninstantiable.
57      */
58     private AttributeTranslator() {
59         // This space intentionally left blank.
60     }
61
62     /**
63      * Gets the list of thrown exceptions for a given method.
64      *
65      * @param method {@code non-null;} the method in question
66      * @return {@code non-null;} the list of thrown exceptions
67      */
68     public static TypeList getExceptions(Method method) {
69         AttributeList attribs = method.getAttributes();
70         AttExceptions exceptions = (AttExceptions)
71             attribs.findFirst(AttExceptions.ATTRIBUTE_NAME);
72
73         if (exceptions == null) {
74             return StdTypeList.EMPTY;
75         }
76
77         return exceptions.getExceptions();
78     }
79
80     /**
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.
85      *
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
88      */
89     public static Annotations getAnnotations(AttributeList attribs) {
90         Annotations result = getAnnotations0(attribs);
91         Annotation signature = getSignature(attribs);
92
93         if (signature != null) {
94             result = Annotations.combine(result, signature);
95         }
96
97         return result;
98     }
99
100     /**
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}
107      * values.
108      *
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
112      */
113     public static Annotations getClassAnnotations(DirectClassFile cf,
114             CfOptions args) {
115         CstType thisClass = cf.getThisClass();
116         AttributeList attribs = cf.getAttributes();
117         Annotations result = getAnnotations(attribs);
118         Annotation enclosingMethod = translateEnclosingMethod(attribs);
119
120         try {
121             Annotations innerClassAnnotations =
122                 translateInnerClasses(thisClass, attribs,
123                         enclosingMethod == null);
124             if (innerClassAnnotations != null) {
125                 result = Annotations.combine(result, innerClassAnnotations);
126             }
127         } catch (Warning warn) {
128             args.warn.println("warning: " + warn.getMessage());
129         }
130
131         if (enclosingMethod != null) {
132             result = Annotations.combine(result, enclosingMethod);
133         }
134
135         if (AccessFlags.isAnnotation(cf.getAccessFlags())) {
136             Annotation annotationDefault =
137                 translateAnnotationDefaults(cf);
138             if (annotationDefault != null) {
139                 result = Annotations.combine(result, annotationDefault);
140             }
141         }
142
143         return result;
144     }
145
146     /**
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}.
150      *
151      * @param method {@code non-null;} the method in question
152      * @return {@code non-null;} the set of annotations, which may be empty
153      */
154     public static Annotations getMethodAnnotations(Method method) {
155         Annotations result = getAnnotations(method.getAttributes());
156         TypeList exceptions = getExceptions(method);
157
158         if (exceptions.size() != 0) {
159             Annotation throwsAnnotation =
160                 AnnotationUtils.makeThrows(exceptions);
161             result = Annotations.combine(result, throwsAnnotation);
162         }
163
164         return result;
165     }
166
167     /**
168      * Helper method for {@link #getAnnotations} which just gets the
169      * existing annotations, per se.
170      *
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
173      */
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);
181
182         if (visible == null) {
183             if (invisible == null) {
184                 return Annotations.EMPTY;
185             }
186             return invisible.getAnnotations();
187         }
188
189         if (invisible == null) {
190             return visible.getAnnotations();
191         }
192
193         // Both are non-null, so combine them.
194
195         return Annotations.combine(visible.getAnnotations(),
196                 invisible.getAnnotations());
197     }
198
199     /**
200      * Gets the {@code Signature} attribute out of a given
201      * {@link AttributeList}, if any, translating it to an annotation.
202      *
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
206      */
207     private static Annotation getSignature(AttributeList attribs) {
208         AttSignature signature = (AttSignature)
209             attribs.findFirst(AttSignature.ATTRIBUTE_NAME);
210
211         if (signature == null) {
212             return null;
213         }
214
215         return AnnotationUtils.makeSignature(signature.getSignature());
216     }
217
218     /**
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.
224      *
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
229      */
230     private static Annotation translateEnclosingMethod(AttributeList attribs) {
231         AttEnclosingMethod enclosingMethod = (AttEnclosingMethod)
232             attribs.findFirst(AttEnclosingMethod.ATTRIBUTE_NAME);
233
234         if (enclosingMethod == null) {
235             return null;
236         }
237
238         CstType enclosingClass = enclosingMethod.getEnclosingClass();
239         CstNat nat = enclosingMethod.getMethod();
240
241         if (nat == null) {
242             /*
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
247              * EnclosingClass.
248              */
249             return AnnotationUtils.makeEnclosingClass(enclosingClass);
250         }
251
252         return AnnotationUtils.makeEnclosingMethod(
253                 new CstMethodRef(enclosingClass, nat));
254     }
255
256     /**
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.
261      *
262      * @param thisClass {@code non-null;} type representing the class being
263      * processed
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
269      */
270     private static Annotations translateInnerClasses(CstType thisClass,
271             AttributeList attribs, boolean needEnclosingClass) {
272         AttInnerClasses innerClasses = (AttInnerClasses)
273             attribs.findFirst(AttInnerClasses.ATTRIBUTE_NAME);
274
275         if (innerClasses == null) {
276             return null;
277         }
278
279         /*
280          * Search the list for the element representing the current class
281          * as well as for any named member classes.
282          */
283
284         InnerClassList list = innerClasses.getInnerClasses();
285         int size = list.size();
286         InnerClassList.Item foundThisClass = null;
287         ArrayList<Type> membersList = new ArrayList<Type>();
288
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());
296             }
297         }
298
299         int membersSize = membersList.size();
300
301         if ((foundThisClass == null) && (membersSize == 0)) {
302             return null;
303         }
304
305         Annotations result = new Annotations();
306
307         if (foundThisClass != null) {
308             result.add(AnnotationUtils.makeInnerClass(
309                                foundThisClass.getInnerName(),
310                                foundThisClass.getAccessFlags()));
311             if (needEnclosingClass) {
312                 CstType outer = foundThisClass.getOuterClass();
313                 if (outer == null) {
314                     throw new Warning(
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.");
330                 }
331                 result.add(AnnotationUtils.makeEnclosingClass(
332                                    foundThisClass.getOuterClass()));
333             }
334         }
335
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));
340             }
341             typeList.setImmutable();
342             result.add(AnnotationUtils.makeMemberClasses(typeList));
343         }
344
345         result.setImmutable();
346         return result;
347     }
348
349     /**
350      * Gets the parameter annotations out of a given method. This
351      * combines both visible and invisible annotations into a single
352      * result set.
353      *
354      * @param method {@code non-null;} the method in question
355      * @return {@code non-null;} the list of annotation sets, which may be
356      * empty
357      */
358     public static AnnotationsList getParameterAnnotations(Method method) {
359         AttributeList attribs = method.getAttributes();
360         AttRuntimeVisibleParameterAnnotations visible =
361             (AttRuntimeVisibleParameterAnnotations)
362             attribs.findFirst(
363                     AttRuntimeVisibleParameterAnnotations.ATTRIBUTE_NAME);
364         AttRuntimeInvisibleParameterAnnotations invisible =
365             (AttRuntimeInvisibleParameterAnnotations)
366             attribs.findFirst(
367                     AttRuntimeInvisibleParameterAnnotations.ATTRIBUTE_NAME);
368
369         if (visible == null) {
370             if (invisible == null) {
371                 return AnnotationsList.EMPTY;
372             }
373             return invisible.getParameterAnnotations();
374         }
375
376         if (invisible == null) {
377             return visible.getParameterAnnotations();
378         }
379
380         // Both are non-null, so combine them.
381
382         return AnnotationsList.combine(visible.getParameterAnnotations(),
383                 invisible.getParameterAnnotations());
384     }
385
386     /**
387      * Gets the {@code AnnotationDefault} attributes out of a
388      * given class, if any, reforming them as an
389      * {@code AnnotationDefault} annotation.
390      *
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
395      */
396     private static Annotation translateAnnotationDefaults(DirectClassFile cf) {
397         CstType thisClass = cf.getThisClass();
398         MethodList methods = cf.getMethods();
399         int sz = methods.size();
400         Annotation result =
401             new Annotation(thisClass, AnnotationVisibility.EMBEDDED);
402         boolean any = false;
403
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);
409
410             if (oneDefault != null) {
411                 NameValuePair pair = new NameValuePair(
412                         one.getNat().getName(),
413                         oneDefault.getValue());
414                 result.add(pair);
415                 any = true;
416             }
417         }
418
419         if (! any) {
420             return null;
421         }
422
423         result.setImmutable();
424         return AnnotationUtils.makeAnnotationDefault(result);
425     }
426 }