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 17package com.android.dx.dex.cf; 18 19import com.android.dx.cf.attrib.AttAnnotationDefault; 20import com.android.dx.cf.attrib.AttEnclosingMethod; 21import com.android.dx.cf.attrib.AttExceptions; 22import com.android.dx.cf.attrib.AttInnerClasses; 23import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations; 24import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations; 25import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations; 26import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations; 27import com.android.dx.cf.attrib.AttSignature; 28import com.android.dx.cf.attrib.InnerClassList; 29import com.android.dx.cf.direct.DirectClassFile; 30import com.android.dx.cf.iface.AttributeList; 31import com.android.dx.cf.iface.Method; 32import com.android.dx.cf.iface.MethodList; 33import com.android.dx.dex.file.AnnotationUtils; 34import com.android.dx.rop.annotation.Annotation; 35import com.android.dx.rop.annotation.AnnotationVisibility; 36import com.android.dx.rop.annotation.Annotations; 37import com.android.dx.rop.annotation.AnnotationsList; 38import com.android.dx.rop.annotation.NameValuePair; 39import com.android.dx.rop.code.AccessFlags; 40import com.android.dx.rop.cst.CstMethodRef; 41import com.android.dx.rop.cst.CstNat; 42import com.android.dx.rop.cst.CstType; 43import com.android.dx.rop.type.StdTypeList; 44import com.android.dx.rop.type.Type; 45import com.android.dx.rop.type.TypeList; 46import com.android.dx.util.Warning; 47 48import 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} 427