1 /* 2 * Copyright (C) 2009 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 signature.converter.dex; 18 19 import java.io.IOException; 20 import java.util.Collections; 21 import java.util.EnumSet; 22 import java.util.HashSet; 23 import java.util.List; 24 import java.util.Set; 25 26 import signature.converter.Visibility; 27 import signature.model.IClassDefinition; 28 import signature.model.Kind; 29 import signature.model.Modifier; 30 import signature.model.impl.SigPackage; 31 import signature.model.util.ModelUtil; 32 import dex.reader.DexBuffer; 33 import dex.reader.DexFileReader; 34 import dex.structure.DexAnnotatedElement; 35 import dex.structure.DexAnnotation; 36 import dex.structure.DexAnnotationAttribute; 37 import dex.structure.DexClass; 38 import dex.structure.DexEncodedValue; 39 import dex.structure.DexField; 40 import dex.structure.DexFile; 41 import dex.structure.DexMethod; 42 43 44 public class DexUtil { 45 46 private static final String PACKAGE_INFO = "package-info"; 47 private static final String THROWS_ANNOTATION = 48 "Ldalvik/annotation/Throws;"; 49 private static final String SIGNATURE_ANNOTATION = 50 "Ldalvik/annotation/Signature;"; 51 private static final String ANNOTATION_DEFAULT_ANNOTATION = 52 "Ldalvik/annotation/AnnotationDefault;"; 53 private static final String ENCLOSING_CLASS_ANNOTATION = 54 "Ldalvik/annotation/EnclosingClass;"; 55 private static final String ENCLOSING_METHOD_ANNOTATION = 56 "Ldalvik/annotation/EnclosingMethod;"; 57 private static final String INNER_CLASS_ANNOTATION = 58 "Ldalvik/annotation/InnerClass;"; 59 private static final String MEMBER_CLASS_ANNOTATION = 60 "Ldalvik/annotation/MemberClasses;"; 61 private static final String JAVA_LANG_OBJECT = "Ljava/lang/Object;"; 62 63 private static final Set<String> INTERNAL_ANNOTATION_NAMES; 64 65 static { 66 Set<String> tmp = new HashSet<String>(); 67 tmp.add(THROWS_ANNOTATION); 68 tmp.add(SIGNATURE_ANNOTATION); 69 tmp.add(ANNOTATION_DEFAULT_ANNOTATION); 70 tmp.add(ENCLOSING_CLASS_ANNOTATION); 71 tmp.add(ENCLOSING_METHOD_ANNOTATION); 72 tmp.add(INNER_CLASS_ANNOTATION); 73 tmp.add(MEMBER_CLASS_ANNOTATION); 74 INTERNAL_ANNOTATION_NAMES = Collections.unmodifiableSet(tmp); 75 } 76 77 private DexUtil() { 78 // not constructable from outside 79 } 80 81 /** 82 * "La/b/c/A;" -> "a.b.c" "LA;" -> "" empty string 83 * 84 * @param classIdentifier 85 * @return the package name 86 */ 87 public static String getPackageName(String classIdentifier) { 88 String name = removeTrailingSemicolon(removeHeadingL(classIdentifier)); 89 return ModelUtil.getPackageName(name.replace("/", ".")); 90 } 91 92 /** 93 * "La/b/c/A;" -> "A" "LA;" -> "A" 94 * 95 * @param classIdentifier 96 * the dalvik internal identifier 97 * @return the class name 98 */ 99 public static String getClassName(String classIdentifier) { 100 String name = removeTrailingSemicolon(removeHeadingL(classIdentifier)); 101 return ModelUtil.getClassName(name.replace("/", ".")).replace('$', '.'); 102 } 103 104 public static String getQualifiedName(String classIdentifier) { 105 String name = removeTrailingSemicolon(removeHeadingL(classIdentifier)); 106 return name.replace('/', '.'); 107 } 108 109 private static String removeHeadingL(String className) { 110 assert className.startsWith("L"); 111 return className.substring(1); 112 } 113 114 private static String removeTrailingSemicolon(String className) { 115 assert className.endsWith(";"); 116 return className.substring(0, className.length() - 1); 117 } 118 119 public static String getDexName(String packageName, String className) { 120 return "L" + packageName.replace('.', '/') + "/" 121 + className.replace('.', '$') + ";"; 122 } 123 124 public static String getDexName(IClassDefinition sigClass) { 125 return getDexName(sigClass.getPackageName(), sigClass.getName()); 126 } 127 128 /** 129 * Returns correct modifiers for inner classes 130 */ 131 public static int getClassModifiers(DexClass clazz) { 132 int modifiers = 0; 133 if (isInnerClass(clazz)) { 134 Integer accessFlags = (Integer) getAnnotationAttributeValue( 135 getAnnotation(clazz, INNER_CLASS_ANNOTATION), 136 "accessFlags"); 137 modifiers = accessFlags.intValue(); 138 } else { 139 modifiers = clazz.getModifiers(); 140 } 141 return modifiers; 142 } 143 144 /** 145 * Returns a set containing all modifiers for the given int. 146 * 147 * @param mod 148 * the original bit coded modifiers as specified by 149 * {@link java.lang.reflect.Modifier} 150 * @return a set containing {@link signature.model.Modifier} elements 151 */ 152 public static Set<Modifier> getModifier(int mod) { 153 Set<Modifier> modifiers = EnumSet.noneOf(Modifier.class); 154 if (java.lang.reflect.Modifier.isAbstract(mod)) 155 modifiers.add(Modifier.ABSTRACT); 156 if (java.lang.reflect.Modifier.isFinal(mod)) 157 modifiers.add(Modifier.FINAL); 158 // if (java.lang.reflect.Modifier.isNative(mod)) 159 // modifiers.add(Modifier.NATIVE); 160 if (java.lang.reflect.Modifier.isPrivate(mod)) 161 modifiers.add(Modifier.PRIVATE); 162 if (java.lang.reflect.Modifier.isProtected(mod)) 163 modifiers.add(Modifier.PROTECTED); 164 if (java.lang.reflect.Modifier.isPublic(mod)) 165 modifiers.add(Modifier.PUBLIC); 166 if (java.lang.reflect.Modifier.isStatic(mod)) 167 modifiers.add(Modifier.STATIC); 168 // if (java.lang.reflect.Modifier.isStrict(mod)) 169 // modifiers.add(Modifier.STRICT); 170 // if (java.lang.reflect.Modifier.isSynchronized(mod)) 171 // modifiers.add(Modifier.SYNCHRONIZED); 172 // if (java.lang.reflect.Modifier.isTransient(mod)) 173 // modifiers.add(Modifier.TRANSIENT); 174 if (java.lang.reflect.Modifier.isVolatile(mod)) 175 modifiers.add(Modifier.VOLATILE); 176 177 return modifiers; 178 } 179 180 /** 181 * Returns true if the given class is an enumeration, false otherwise. 182 * 183 * @param dexClass 184 * the DexClass under test 185 * @return true if the given class is an enumeration, false otherwise 186 */ 187 public static boolean isEnum(DexClass dexClass) { 188 return (getClassModifiers(dexClass) & 0x4000) > 0; 189 } 190 191 /** 192 * Returns true if the given class is an interface, false otherwise. 193 * 194 * @param dexClass 195 * the DexClass under test 196 * @return true if the given class is an interface, false otherwise 197 */ 198 public static boolean isInterface(DexClass dexClass) { 199 int modifiers = getClassModifiers(dexClass); 200 return java.lang.reflect.Modifier.isInterface(modifiers); 201 } 202 203 /** 204 * Returns true if the given class is an annotation, false otherwise. 205 * 206 * @param dexClass 207 * the DexClass under test 208 * @return true if the given class is an annotation, false otherwise 209 */ 210 public static boolean isAnnotation(DexClass dexClass) { 211 return (getClassModifiers(dexClass) & 0x2000) > 0; 212 } 213 214 public static boolean isSynthetic(int modifier) { 215 return (modifier & 0x1000) > 0; 216 } 217 218 /** 219 * Returns the Kind of the given DexClass. 220 * 221 * @param dexClass 222 * the DexClass under test 223 * @return the Kind of the given class 224 */ 225 public static Kind getKind(DexClass dexClass) { 226 // order of branches is crucial since a annotation is also an interface 227 if (isEnum(dexClass)) { 228 return Kind.ENUM; 229 } else if (isAnnotation(dexClass)) { 230 return Kind.ANNOTATION; 231 } else if (isInterface(dexClass)) { 232 return Kind.INTERFACE; 233 } else { 234 return Kind.CLASS; 235 } 236 } 237 238 /** 239 * Returns whether the specified annotated element has an annotation with 240 * type "Ldalvik/annotation/Throws;". 241 * 242 * @param annotatedElement 243 * the annotated element to check 244 * @return <code>true</code> if the given annotated element has the 245 * mentioned annotation, false otherwise 246 */ 247 public static boolean declaresExceptions( 248 DexAnnotatedElement annotatedElement) { 249 return getAnnotation(annotatedElement, THROWS_ANNOTATION) != null; 250 } 251 252 /** 253 * Returns the throws signature if the given element has such an annotation, 254 * null otherwise. 255 * 256 * @param annotatedElement 257 * the annotated element 258 * @return he generic signature if the given element has such an annotation, 259 * null otherwise 260 */ 261 @SuppressWarnings("unchecked") 262 public static String getExceptionSignature( 263 DexAnnotatedElement annotatedElement) { 264 DexAnnotation annotation = getAnnotation(annotatedElement, 265 THROWS_ANNOTATION); 266 if (annotation != null) { 267 List<DexEncodedValue> value = 268 (List<DexEncodedValue>) getAnnotationAttributeValue( 269 annotation, "value"); 270 return concatEncodedValues(value); 271 } 272 return null; 273 } 274 275 /** 276 * Splits a list of types: 277 * "Ljava/io/IOException;Ljava/lang/IllegalStateException;" <br> 278 * into separate type designators: <br> 279 * "Ljava/io/IOException;" , "Ljava/lang/IllegalStateException;" 280 * 281 * @param typeList 282 * the type list 283 * @return a set of type designators 284 */ 285 public static Set<String> splitTypeList(String typeList) { 286 String[] split = typeList.split(";"); 287 Set<String> separateTypes = new HashSet<String>(); 288 for (String string : split) { 289 separateTypes.add(string + ";");// add semicolon again 290 } 291 return separateTypes; 292 } 293 294 /** 295 * Returns whether the specified annotated element has an annotation with 296 * type "Ldalvik/annotation/Signature;". 297 * 298 * @param annotatedElement 299 * the annotated element to check 300 * @return <code>true</code> if the given annotated element has the 301 * mentioned annotation, false otherwise 302 */ 303 public static boolean hasGenericSignature( 304 DexAnnotatedElement annotatedElement) { 305 return getAnnotation(annotatedElement, SIGNATURE_ANNOTATION) != null; 306 } 307 308 /** 309 * Returns the generic signature if the given element has such an 310 * annotation, null otherwise. 311 * 312 * @param annotatedElement 313 * the annotated element 314 * @return he generic signature if the given element has such an annotation, 315 * null otherwise 316 */ 317 @SuppressWarnings("unchecked") 318 public static String getGenericSignature( 319 DexAnnotatedElement annotatedElement) { 320 DexAnnotation annotation = getAnnotation(annotatedElement, 321 SIGNATURE_ANNOTATION); 322 if (annotation != null) { 323 List<DexEncodedValue> value = 324 (List<DexEncodedValue>) getAnnotationAttributeValue( 325 annotation, "value"); 326 return concatEncodedValues(value); 327 } 328 return null; 329 } 330 331 /** 332 * Returns whether the specified annotated element has an annotation with 333 * type "Ldalvik/annotation/AnnotationDefault;". 334 * 335 * @param annotatedElement 336 * the annotated element to check 337 * @return <code>true</code> if the given annotated element has the 338 * mentioned annotation, false otherwise 339 */ 340 public static boolean hasAnnotationDefaultSignature( 341 DexAnnotatedElement annotatedElement) { 342 return getAnnotation( 343 annotatedElement, ANNOTATION_DEFAULT_ANNOTATION)!= null; 344 } 345 346 /** 347 * Returns a mapping form annotation attribute name to its default value. 348 * 349 * @param dexClass 350 * the class defining a annotation 351 * @return a mapping form annotation attribute name to its default value 352 */ 353 public static DexAnnotation getDefaultMappingsAnnotation( 354 DexClass dexClass) { 355 return getAnnotation(dexClass, ANNOTATION_DEFAULT_ANNOTATION); 356 } 357 358 /** 359 * Returns the annotation with the specified type from the given element or 360 * null if no such annotation is available. 361 * 362 * @param element 363 * the annotated element 364 * @param annotationType 365 * the dex internal name of the annotation type 366 * @return the annotation with the specified type or null if not present 367 */ 368 public static DexAnnotation getAnnotation(DexAnnotatedElement element, 369 String annotationType) { 370 assert element != null; 371 assert annotationType != null; 372 373 for (DexAnnotation anno : element.getAnnotations()) { 374 if (annotationType.equals(anno.getTypeName())) { 375 return anno; 376 } 377 } 378 return null; 379 } 380 381 /** 382 * Returns the value for the specified attribute name of the given 383 * annotation or null if not present. 384 * 385 * @param annotation 386 * the annotation 387 * @param attributeName 388 * the name of the attribute 389 * @return the value for the specified attribute 390 */ 391 public static Object getAnnotationAttributeValue(DexAnnotation annotation, 392 String attributeName) { 393 for (DexAnnotationAttribute dexAnnotationAttribute : annotation 394 .getAttributes()) { 395 if (attributeName.equals(dexAnnotationAttribute.getName())) { 396 return dexAnnotationAttribute.getEncodedValue().getValue(); 397 } 398 } 399 return null; 400 } 401 402 private static String concatEncodedValues(List<DexEncodedValue> values) { 403 StringBuilder builder = new StringBuilder(); 404 for (DexEncodedValue string : values) { 405 builder.append(string.getValue()); 406 } 407 return builder.toString(); 408 } 409 410 /** 411 * Returns true if the given method is a constructor, false otherwise. 412 * 413 * @param method 414 * the method to test 415 * @return true if the given method is a constructor, false otherwise 416 */ 417 public static boolean isConstructor(DexMethod method) { 418 return "<init>".equals(method.getName()); 419 } 420 421 /** 422 * Returns true if the given method is a static constructor, false 423 * otherwise. 424 * 425 * @param method 426 * the method to test 427 * @return true if the given method is a static constructor, false otherwise 428 */ 429 public static boolean isStaticConstructor(DexMethod method) { 430 return "<clinit>".equals(method.getName()); 431 } 432 433 public static boolean isMethod(DexMethod method) { 434 return !isConstructor(method) && !isStaticConstructor(method); 435 } 436 437 /** 438 * Returns the package-info class for the given package. 439 * 440 * @param aPackage 441 * the package 442 * @return the class called "package-info" or null, if not available 443 */ 444 public static IClassDefinition findPackageInfo(SigPackage aPackage) { 445 for (IClassDefinition clazz : aPackage.getClasses()) { 446 if (PACKAGE_INFO.equals(clazz.getName())) { 447 return clazz; 448 } 449 } 450 return null; 451 } 452 453 public static boolean isPackageInfo(DexClass clazz) { 454 return PACKAGE_INFO.equals(getClassName(clazz.getName())); 455 } 456 457 public static boolean isInternalAnnotation(DexAnnotation dexAnnotation) { 458 return INTERNAL_ANNOTATION_NAMES.contains(dexAnnotation.getTypeName()); 459 } 460 461 /** 462 * An InnerClass annotation is attached to each class which is defined in 463 * the lexical scope of another class's definition. Any class which has this 464 * annotation must also have either an EnclosingClass annotation or an 465 * EnclosingMethod annotation. 466 */ 467 public static boolean isInnerClass(DexClass clazz) { 468 return getAnnotation(clazz, INNER_CLASS_ANNOTATION) != null; 469 } 470 471 /** 472 * An EnclosingClass annotation is attached to each class which is either 473 * defined as a member of another class, per se, or is anonymous but not 474 * defined within a method body (e.g., a synthetic inner class). Every class 475 * that has this annotation must also have an InnerClass annotation. 476 * Additionally, a class may not have both an EnclosingClass and an 477 * EnclosingMethod annotation. 478 */ 479 public static boolean isEnclosingClass(DexClass clazz) { 480 return getAnnotation(clazz, ENCLOSING_CLASS_ANNOTATION) != null; 481 } 482 483 public static boolean declaresMemberClasses(DexClass dexClass) { 484 return getAnnotation(dexClass, MEMBER_CLASS_ANNOTATION) != null; 485 } 486 487 @SuppressWarnings("unchecked") 488 public static Set<String> getMemberClassNames(DexClass dexClass) { 489 DexAnnotation annotation = getAnnotation(dexClass, 490 MEMBER_CLASS_ANNOTATION); 491 List<DexEncodedValue> enclosedClasses = 492 (List<DexEncodedValue>) getAnnotationAttributeValue( 493 annotation, "value"); 494 Set<String> enclosedClassesNames = new HashSet<String>(); 495 for (DexEncodedValue string : enclosedClasses) { 496 enclosedClassesNames.add((String) string.getValue()); 497 } 498 return enclosedClassesNames; 499 } 500 501 502 public static String getEnclosingClassName(DexClass dexClass) { 503 DexAnnotation annotation = getAnnotation(dexClass, 504 ENCLOSING_CLASS_ANNOTATION); 505 String value = (String) getAnnotationAttributeValue(annotation, 506 "value"); 507 return value; 508 } 509 510 public static boolean convertAnyWay(DexClass dexClass) { 511 return !isSynthetic(getClassModifiers(dexClass)) 512 && !isAnonymousClassName(dexClass.getName()) 513 || isPackageInfo(dexClass); 514 } 515 516 public static boolean isVisible(DexClass dexClass, Visibility visibility) { 517 // package info is always visible 518 if (isPackageInfo(dexClass)) { 519 return true; 520 } 521 522 if (isDeclaredInMethod(dexClass)) { 523 return false; 524 } 525 526 if (isAnonymousClassName(dexClass.getName())) { 527 return false; 528 } 529 530 int modifiers = getClassModifiers(dexClass); 531 532 return isVisible(modifiers, visibility); 533 } 534 535 private static boolean isDeclaredInMethod(DexClass dexClass) { 536 return getAnnotation(dexClass, ENCLOSING_METHOD_ANNOTATION) != null; 537 } 538 539 /** 540 * Returns whether the given dex identifier is an anonymous class name. 541 * Format: La/b/C$1; 542 * 543 * @param dexName 544 * the name to analyze 545 * @return whether the given dex identifier is an anonymous class name 546 */ 547 public static boolean isAnonymousClassName(String dexName) { 548 int index = dexName.lastIndexOf('$'); 549 return (index != 0) ? Character.isDigit(dexName.charAt(index + 1)) 550 : false; 551 } 552 553 public static boolean isVisible(DexField dexField, Visibility visibility) { 554 return isVisible(dexField.getModifiers(), visibility); 555 } 556 557 public static boolean isVisible(DexMethod dexMethod, 558 Visibility visibility) { 559 return isVisible(dexMethod.getModifiers(), visibility); 560 } 561 562 private static boolean isVisible(int modifiers, Visibility visibility) { 563 564 if (isSynthetic(modifiers)) { 565 return false; 566 } 567 568 Set<Modifier> elementModifiers = getModifier(modifiers); 569 if (elementModifiers.contains(Modifier.PUBLIC)) { 570 return true; 571 } else if (elementModifiers.contains(Modifier.PROTECTED)) { 572 return visibility == Visibility.PROTECTED 573 || visibility == Visibility.PACKAGE 574 || visibility == Visibility.PRIVATE; 575 } else if (elementModifiers.contains(Modifier.PRIVATE)) { 576 return visibility == Visibility.PRIVATE; 577 } else { 578 return visibility == Visibility.PACKAGE 579 || visibility == Visibility.PRIVATE; 580 } 581 } 582 583 public static Set<DexFile> getDexFiles(Set<String> fileNames) 584 throws IOException { 585 Set<DexFile> parsedFiles = new HashSet<DexFile>(); 586 587 for (String dexFile : fileNames) { 588 DexFileReader reader = new DexFileReader(); 589 DexBuffer dexBuffer = new DexBuffer(dexFile); 590 parsedFiles.add(reader.read(dexBuffer)); 591 } 592 return parsedFiles; 593 } 594 595 596 public static boolean isJavaLangObject(DexClass dexClass) { 597 assert dexClass != null; 598 return JAVA_LANG_OBJECT.equals(dexClass.getName()); 599 } 600 } 601