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 import java.util.ArrayList; 48 49 /** 50 * Utility methods that translate various classfile attributes 51 * into forms suitable for use in creating {@code dex} files. 52 */ 53 /*package*/ class AttributeTranslator { 54 /** 55 * This class is uninstantiable. 56 */ 57 private AttributeTranslator() { 58 // This space intentionally left blank. 59 } 60 61 /** 62 * Gets the list of thrown exceptions for a given method. 63 * 64 * @param method {@code non-null;} the method in question 65 * @return {@code non-null;} the list of thrown exceptions 66 */ 67 public static TypeList getExceptions(Method method) { 68 AttributeList attribs = method.getAttributes(); 69 AttExceptions exceptions = (AttExceptions) 70 attribs.findFirst(AttExceptions.ATTRIBUTE_NAME); 71 72 if (exceptions == null) { 73 return StdTypeList.EMPTY; 74 } 75 76 return exceptions.getExceptions(); 77 } 78 79 /** 80 * Gets the annotations out of a given {@link AttributeList}. This 81 * combines both visible and invisible annotations into a single 82 * result set and also adds in a system annotation for the 83 * {@code Signature} attribute if present. 84 * 85 * @param attribs {@code non-null;} the attributes list to search in 86 * @return {@code non-null;} the set of annotations, which may be empty 87 */ 88 public static Annotations getAnnotations(AttributeList attribs) { 89 Annotations result = getAnnotations0(attribs); 90 Annotation signature = getSignature(attribs); 91 92 if (signature != null) { 93 result = Annotations.combine(result, signature); 94 } 95 96 return result; 97 } 98 99 /** 100 * Gets the annotations out of a given class, similar to {@link 101 * #getAnnotations}, also including annotations for translations 102 * of class-level attributes {@code EnclosingMethod} and 103 * {@code InnerClasses}, if present. Additionally, if the 104 * class is an annotation class, then this also includes a 105 * representation of all the {@code AnnotationDefault} 106 * values. 107 * 108 * @param cf {@code non-null;} the class in question 109 * @param args {@code non-null;} the high-level options 110 * @return {@code non-null;} the set of annotations, which may be empty 111 */ 112 public static Annotations getClassAnnotations(DirectClassFile cf, 113 CfOptions args) { 114 CstType thisClass = cf.getThisClass(); 115 AttributeList attribs = cf.getAttributes(); 116 Annotations result = getAnnotations(attribs); 117 Annotation enclosingMethod = translateEnclosingMethod(attribs); 118 119 try { 120 Annotations innerClassAnnotations = 121 translateInnerClasses(thisClass, attribs, 122 enclosingMethod == null); 123 if (innerClassAnnotations != null) { 124 result = Annotations.combine(result, innerClassAnnotations); 125 } 126 } catch (Warning warn) { 127 args.warn.println("warning: " + warn.getMessage()); 128 } 129 130 if (enclosingMethod != null) { 131 result = Annotations.combine(result, enclosingMethod); 132 } 133 134 if (AccessFlags.isAnnotation(cf.getAccessFlags())) { 135 Annotation annotationDefault = 136 translateAnnotationDefaults(cf); 137 if (annotationDefault != null) { 138 result = Annotations.combine(result, annotationDefault); 139 } 140 } 141 142 return result; 143 } 144 145 /** 146 * Gets the annotations out of a given method, similar to {@link 147 * #getAnnotations}, also including an annotation for the translation 148 * of the method-specific attribute {@code Exceptions}. 149 * 150 * @param method {@code non-null;} the method in question 151 * @return {@code non-null;} the set of annotations, which may be empty 152 */ 153 public static Annotations getMethodAnnotations(Method method) { 154 Annotations result = getAnnotations(method.getAttributes()); 155 TypeList exceptions = getExceptions(method); 156 157 if (exceptions.size() != 0) { 158 Annotation throwsAnnotation = 159 AnnotationUtils.makeThrows(exceptions); 160 result = Annotations.combine(result, throwsAnnotation); 161 } 162 163 return result; 164 } 165 166 /** 167 * Helper method for {@link #getAnnotations} which just gets the 168 * existing annotations, per se. 169 * 170 * @param attribs {@code non-null;} the attributes list to search in 171 * @return {@code non-null;} the set of annotations, which may be empty 172 */ 173 private static Annotations getAnnotations0(AttributeList attribs) { 174 AttRuntimeVisibleAnnotations visible = 175 (AttRuntimeVisibleAnnotations) 176 attribs.findFirst(AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME); 177 AttRuntimeInvisibleAnnotations invisible = 178 (AttRuntimeInvisibleAnnotations) 179 attribs.findFirst(AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME); 180 181 if (visible == null) { 182 if (invisible == null) { 183 return Annotations.EMPTY; 184 } 185 return invisible.getAnnotations(); 186 } 187 188 if (invisible == null) { 189 return visible.getAnnotations(); 190 } 191 192 // Both are non-null, so combine them. 193 194 return Annotations.combine(visible.getAnnotations(), 195 invisible.getAnnotations()); 196 } 197 198 /** 199 * Gets the {@code Signature} attribute out of a given 200 * {@link AttributeList}, if any, translating it to an annotation. 201 * 202 * @param attribs {@code non-null;} the attributes list to search in 203 * @return {@code null-ok;} the converted {@code Signature} annotation, 204 * if there was an attribute to translate 205 */ 206 private static Annotation getSignature(AttributeList attribs) { 207 AttSignature signature = (AttSignature) 208 attribs.findFirst(AttSignature.ATTRIBUTE_NAME); 209 210 if (signature == null) { 211 return null; 212 } 213 214 return AnnotationUtils.makeSignature(signature.getSignature()); 215 } 216 217 /** 218 * Gets the {@code EnclosingMethod} attribute out of a given 219 * {@link AttributeList}, if any, translating it to an annotation. 220 * If the class really has an enclosing method, this returns an 221 * {@code EnclosingMethod} annotation; if not, this returns 222 * an {@code EnclosingClass} annotation. 223 * 224 * @param attribs {@code non-null;} the attributes list to search in 225 * @return {@code null-ok;} the converted {@code EnclosingMethod} or 226 * {@code EnclosingClass} annotation, if there was an 227 * attribute to translate 228 */ 229 private static Annotation translateEnclosingMethod(AttributeList attribs) { 230 AttEnclosingMethod enclosingMethod = (AttEnclosingMethod) 231 attribs.findFirst(AttEnclosingMethod.ATTRIBUTE_NAME); 232 233 if (enclosingMethod == null) { 234 return null; 235 } 236 237 CstType enclosingClass = enclosingMethod.getEnclosingClass(); 238 CstNat nat = enclosingMethod.getMethod(); 239 240 if (nat == null) { 241 /* 242 * Dalvik doesn't use EnclosingMethod annotations unless 243 * there really is an enclosing method. Anonymous classes 244 * are unambiguously identified by having an InnerClass 245 * annotation with an empty name along with an appropriate 246 * EnclosingClass. 247 */ 248 return AnnotationUtils.makeEnclosingClass(enclosingClass); 249 } 250 251 return AnnotationUtils.makeEnclosingMethod( 252 new CstMethodRef(enclosingClass, nat)); 253 } 254 255 /** 256 * Gets the {@code InnerClasses} attribute out of a given 257 * {@link AttributeList}, if any, translating it to one or more of an 258 * {@code InnerClass}, {@code EnclosingClass}, or 259 * {@code MemberClasses} annotation. 260 * 261 * @param thisClass {@code non-null;} type representing the class being 262 * processed 263 * @param attribs {@code non-null;} the attributes list to search in 264 * @param needEnclosingClass whether to include an 265 * {@code EnclosingClass} annotation 266 * @return {@code null-ok;} the converted list of annotations, if there 267 * was an attribute to translate 268 */ 269 private static Annotations translateInnerClasses(CstType thisClass, 270 AttributeList attribs, boolean needEnclosingClass) { 271 AttInnerClasses innerClasses = (AttInnerClasses) 272 attribs.findFirst(AttInnerClasses.ATTRIBUTE_NAME); 273 274 if (innerClasses == null) { 275 return null; 276 } 277 278 /* 279 * Search the list for the element representing the current class 280 * as well as for any named member classes. 281 */ 282 283 InnerClassList list = innerClasses.getInnerClasses(); 284 int size = list.size(); 285 InnerClassList.Item foundThisClass = null; 286 ArrayList<Type> membersList = new ArrayList<Type>(); 287 288 for (int i = 0; i < size; i++) { 289 InnerClassList.Item item = list.get(i); 290 CstType innerClass = item.getInnerClass(); 291 if (innerClass.equals(thisClass)) { 292 foundThisClass = item; 293 } else if (thisClass.equals(item.getOuterClass())) { 294 membersList.add(innerClass.getClassType()); 295 } 296 } 297 298 int membersSize = membersList.size(); 299 300 if ((foundThisClass == null) && (membersSize == 0)) { 301 return null; 302 } 303 304 Annotations result = new Annotations(); 305 306 if (foundThisClass != null) { 307 result.add(AnnotationUtils.makeInnerClass( 308 foundThisClass.getInnerName(), 309 foundThisClass.getAccessFlags())); 310 if (needEnclosingClass) { 311 CstType outer = foundThisClass.getOuterClass(); 312 if (outer == null) { 313 throw new Warning( 314 "Ignoring InnerClasses attribute for an " + 315 "anonymous inner class\n" + 316 "(" + thisClass.toHuman() + 317 ") that doesn't come with an\n" + 318 "associated EnclosingMethod attribute. " + 319 "This class was probably produced by a\n" + 320 "compiler that did not target the modern " + 321 ".class file format. The recommended\n" + 322 "solution is to recompile the class from " + 323 "source, using an up-to-date compiler\n" + 324 "and without specifying any \"-target\" type " + 325 "options. The consequence of ignoring\n" + 326 "this warning is that reflective operations " + 327 "on this class will incorrectly\n" + 328 "indicate that it is *not* an inner class."); 329 } 330 result.add(AnnotationUtils.makeEnclosingClass( 331 foundThisClass.getOuterClass())); 332 } 333 } 334 335 if (membersSize != 0) { 336 StdTypeList typeList = new StdTypeList(membersSize); 337 for (int i = 0; i < membersSize; i++) { 338 typeList.set(i, membersList.get(i)); 339 } 340 typeList.setImmutable(); 341 result.add(AnnotationUtils.makeMemberClasses(typeList)); 342 } 343 344 result.setImmutable(); 345 return result; 346 } 347 348 /** 349 * Gets the parameter annotations out of a given method. This 350 * combines both visible and invisible annotations into a single 351 * result set. 352 * 353 * @param method {@code non-null;} the method in question 354 * @return {@code non-null;} the list of annotation sets, which may be 355 * empty 356 */ 357 public static AnnotationsList getParameterAnnotations(Method method) { 358 AttributeList attribs = method.getAttributes(); 359 AttRuntimeVisibleParameterAnnotations visible = 360 (AttRuntimeVisibleParameterAnnotations) 361 attribs.findFirst( 362 AttRuntimeVisibleParameterAnnotations.ATTRIBUTE_NAME); 363 AttRuntimeInvisibleParameterAnnotations invisible = 364 (AttRuntimeInvisibleParameterAnnotations) 365 attribs.findFirst( 366 AttRuntimeInvisibleParameterAnnotations.ATTRIBUTE_NAME); 367 368 if (visible == null) { 369 if (invisible == null) { 370 return AnnotationsList.EMPTY; 371 } 372 return invisible.getParameterAnnotations(); 373 } 374 375 if (invisible == null) { 376 return visible.getParameterAnnotations(); 377 } 378 379 // Both are non-null, so combine them. 380 381 return AnnotationsList.combine(visible.getParameterAnnotations(), 382 invisible.getParameterAnnotations()); 383 } 384 385 /** 386 * Gets the {@code AnnotationDefault} attributes out of a 387 * given class, if any, reforming them as an 388 * {@code AnnotationDefault} annotation. 389 * 390 * @param cf {@code non-null;} the class in question 391 * @return {@code null-ok;} an appropriately-constructed 392 * {@code AnnotationDefault} annotation, if there were any 393 * annotation defaults in the class, or {@code null} if not 394 */ 395 private static Annotation translateAnnotationDefaults(DirectClassFile cf) { 396 CstType thisClass = cf.getThisClass(); 397 MethodList methods = cf.getMethods(); 398 int sz = methods.size(); 399 Annotation result = 400 new Annotation(thisClass, AnnotationVisibility.EMBEDDED); 401 boolean any = false; 402 403 for (int i = 0; i < sz; i++) { 404 Method one = methods.get(i); 405 AttributeList attribs = one.getAttributes(); 406 AttAnnotationDefault oneDefault = (AttAnnotationDefault) 407 attribs.findFirst(AttAnnotationDefault.ATTRIBUTE_NAME); 408 409 if (oneDefault != null) { 410 NameValuePair pair = new NameValuePair( 411 one.getNat().getName(), 412 oneDefault.getValue()); 413 result.add(pair); 414 any = true; 415 } 416 } 417 418 if (! any) { 419 return null; 420 } 421 422 result.setImmutable(); 423 return AnnotationUtils.makeAnnotationDefault(result); 424 } 425 } 426