1 // Copyright 2018 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package com.google.devtools.build.android.desugar.scan; 15 16 import static com.google.common.base.Preconditions.checkArgument; 17 18 import com.google.common.collect.ImmutableSet; 19 import javax.annotation.Nullable; 20 import org.objectweb.asm.AnnotationVisitor; 21 import org.objectweb.asm.ClassReader; 22 import org.objectweb.asm.ClassVisitor; 23 import org.objectweb.asm.FieldVisitor; 24 import org.objectweb.asm.Handle; 25 import org.objectweb.asm.Label; 26 import org.objectweb.asm.MethodVisitor; 27 import org.objectweb.asm.Opcodes; 28 import org.objectweb.asm.Type; 29 import org.objectweb.asm.TypePath; 30 31 /** {@link ClassVisitor} that records references to classes starting with a given prefix. */ 32 class PrefixReferenceScanner extends ClassVisitor { 33 34 /** 35 * Returns references with the given prefix in the given class. 36 * 37 * @param prefix an internal name prefix, typically a package such as {@code com/google/} 38 */ 39 public static ImmutableSet<KeepReference> scan(ClassReader reader, String prefix) { 40 PrefixReferenceScanner scanner = new PrefixReferenceScanner(prefix); 41 // Frames irrelevant for Android so skip them. Don't skip debug info in case the class we're 42 // visiting has local variable tables (typically it doesn't anyways). 43 reader.accept(scanner, ClassReader.SKIP_FRAMES); 44 return scanner.roots.build(); 45 } 46 47 private final ImmutableSet.Builder<KeepReference> roots = ImmutableSet.builder(); 48 private final PrefixReferenceMethodVisitor mv = new PrefixReferenceMethodVisitor(); 49 private final PrefixReferenceFieldVisitor fv = new PrefixReferenceFieldVisitor(); 50 private final PrefixReferenceAnnotationVisitor av = new PrefixReferenceAnnotationVisitor(); 51 52 private final String prefix; 53 54 public PrefixReferenceScanner(String prefix) { 55 super(Opcodes.ASM6); 56 this.prefix = prefix; 57 } 58 59 @Override 60 public void visit( 61 int version, 62 int access, 63 String name, 64 String signature, 65 String superName, 66 String[] interfaces) { 67 checkArgument(!name.startsWith(prefix)); 68 if (superName != null) { 69 classReference(superName); 70 } 71 classReferences(interfaces); 72 super.visit(version, access, name, signature, superName, interfaces); 73 } 74 75 @Override 76 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 77 typeReference(desc); 78 return av; 79 } 80 81 @Override 82 public void visitOuterClass(String owner, String name, String desc) { 83 classReference(owner); 84 if (desc != null) { 85 typeReference(Type.getMethodType(desc)); 86 } 87 } 88 89 @Override 90 public AnnotationVisitor visitTypeAnnotation( 91 int typeRef, TypePath typePath, String desc, boolean visible) { 92 typeReference(desc); 93 return av; 94 } 95 96 @Override 97 public void visitInnerClass(String name, String outerName, String innerName, int access) { 98 classReference(name); 99 if (outerName != null) { 100 classReference(outerName); 101 } 102 } 103 104 @Override 105 public FieldVisitor visitField( 106 int access, String name, String desc, String signature, Object value) { 107 typeReference(desc); 108 return fv; 109 } 110 111 @Override 112 public MethodVisitor visitMethod( 113 int access, String name, String desc, String signature, String[] exceptions) { 114 typeReference(Type.getMethodType(desc)); 115 classReferences(exceptions); 116 return mv; 117 } 118 119 private void classReferences(@Nullable String[] internalNames) { 120 if (internalNames != null) { 121 for (String itf : internalNames) { 122 classReference(itf); 123 } 124 } 125 } 126 127 // The following methods are package-private so they don't incur bridge methods when called from 128 // inner classes below. 129 130 void classReference(String internalName) { 131 checkArgument(internalName.charAt(0) != '[' && internalName.charAt(0) != '(', internalName); 132 checkArgument(!internalName.endsWith(";"), internalName); 133 if (internalName.startsWith(prefix)) { 134 roots.add(KeepReference.classReference(internalName)); 135 } 136 } 137 138 void objectReference(String internalName) { 139 // don't call this for method types, convert to Type instead 140 checkArgument(internalName.charAt(0) != '(', internalName); 141 if (internalName.charAt(0) == '[') { 142 typeReference(internalName); 143 } else { 144 classReference(internalName); 145 } 146 } 147 148 void typeReference(String typeDesc) { 149 // don't call this for method types, convert to Type instead 150 checkArgument(typeDesc.charAt(0) != '(', typeDesc); 151 152 int lpos = typeDesc.lastIndexOf('[') + 1; 153 if (typeDesc.charAt(lpos) == 'L') { 154 checkArgument(typeDesc.endsWith(";"), typeDesc); 155 classReference(typeDesc.substring(lpos, typeDesc.length() - 1)); 156 } else { 157 // else primitive or primitive array 158 checkArgument(typeDesc.length() == lpos + 1, typeDesc); 159 switch (typeDesc.charAt(lpos)) { 160 case 'B': 161 case 'C': 162 case 'S': 163 case 'I': 164 case 'J': 165 case 'D': 166 case 'F': 167 case 'Z': 168 break; 169 default: 170 throw new AssertionError("Unexpected type descriptor: " + typeDesc); 171 } 172 } 173 } 174 175 void typeReference(Type type) { 176 switch (type.getSort()) { 177 case Type.ARRAY: 178 typeReference(type.getElementType()); 179 break; 180 case Type.OBJECT: 181 classReference(type.getInternalName()); 182 break; 183 184 case Type.METHOD: 185 for (Type param : type.getArgumentTypes()) { 186 typeReference(param); 187 } 188 typeReference(type.getReturnType()); 189 break; 190 191 default: 192 break; 193 } 194 } 195 196 void fieldReference(String owner, String name, String desc) { 197 objectReference(owner); 198 typeReference(desc); 199 if (owner.startsWith(prefix)) { 200 roots.add(KeepReference.memberReference(owner, name, desc)); 201 } 202 } 203 204 void methodReference(String owner, String name, String desc) { 205 checkArgument(desc.charAt(0) == '(', desc); 206 objectReference(owner); 207 typeReference(Type.getMethodType(desc)); 208 if (owner.startsWith(prefix)) { 209 roots.add(KeepReference.memberReference(owner, name, desc)); 210 } 211 } 212 213 void handleReference(Handle handle) { 214 switch (handle.getTag()) { 215 case Opcodes.H_GETFIELD: 216 case Opcodes.H_GETSTATIC: 217 case Opcodes.H_PUTFIELD: 218 case Opcodes.H_PUTSTATIC: 219 fieldReference(handle.getOwner(), handle.getName(), handle.getDesc()); 220 break; 221 222 default: 223 methodReference(handle.getOwner(), handle.getName(), handle.getDesc()); 224 break; 225 } 226 } 227 228 private class PrefixReferenceMethodVisitor extends MethodVisitor { 229 230 public PrefixReferenceMethodVisitor() { 231 super(Opcodes.ASM6); 232 } 233 234 @Override 235 public AnnotationVisitor visitAnnotationDefault() { 236 return av; 237 } 238 239 @Override 240 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 241 typeReference(desc); 242 return av; 243 } 244 245 @Override 246 public AnnotationVisitor visitTypeAnnotation( 247 int typeRef, TypePath typePath, String desc, boolean visible) { 248 typeReference(desc); 249 return av; 250 } 251 252 @Override 253 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { 254 typeReference(desc); 255 return av; 256 } 257 258 @Override 259 public void visitTypeInsn(int opcode, String type) { 260 objectReference(type); 261 } 262 263 @Override 264 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 265 fieldReference(owner, name, desc); 266 } 267 268 @Override 269 @SuppressWarnings("deprecation") // Implementing deprecated method to be sure 270 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 271 visitMethodInsn(opcode, owner, name, desc, opcode == Opcodes.INVOKEINTERFACE); 272 } 273 274 @Override 275 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 276 methodReference(owner, name, desc); 277 } 278 279 @Override 280 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { 281 typeReference(Type.getMethodType(desc)); 282 handleReference(bsm); 283 for (Object bsmArg : bsmArgs) { 284 visitConstant(bsmArg); 285 } 286 } 287 288 @Override 289 public void visitLdcInsn(Object cst) { 290 visitConstant(cst); 291 } 292 293 private void visitConstant(Object cst) { 294 if (cst instanceof Type) { 295 typeReference((Type) cst); 296 } else if (cst instanceof Handle) { 297 handleReference((Handle) cst); 298 } else { 299 // Check for other expected types as javadoc recommends 300 checkArgument( 301 cst instanceof String 302 || cst instanceof Integer 303 || cst instanceof Long 304 || cst instanceof Float 305 || cst instanceof Double, 306 "Unexpected constant: ", cst); 307 } 308 } 309 310 @Override 311 public void visitMultiANewArrayInsn(String desc, int dims) { 312 typeReference(desc); 313 } 314 315 @Override 316 public AnnotationVisitor visitInsnAnnotation( 317 int typeRef, TypePath typePath, String desc, boolean visible) { 318 typeReference(desc); 319 return av; 320 } 321 322 @Override 323 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 324 if (type != null) { 325 classReference(type); 326 } 327 } 328 329 @Override 330 public AnnotationVisitor visitTryCatchAnnotation( 331 int typeRef, TypePath typePath, String desc, boolean visible) { 332 typeReference(desc); 333 return av; 334 } 335 336 @Override 337 public void visitLocalVariable( 338 String name, String desc, String signature, Label start, Label end, int index) { 339 typeReference(desc); 340 } 341 342 @Override 343 public AnnotationVisitor visitLocalVariableAnnotation( 344 int typeRef, 345 TypePath typePath, 346 Label[] start, 347 Label[] end, 348 int[] index, 349 String desc, 350 boolean visible) { 351 typeReference(desc); 352 return av; 353 } 354 } 355 356 private class PrefixReferenceFieldVisitor extends FieldVisitor { 357 358 public PrefixReferenceFieldVisitor() { 359 super(Opcodes.ASM6); 360 } 361 362 @Override 363 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 364 typeReference(desc); 365 return av; 366 } 367 368 @Override 369 public AnnotationVisitor visitTypeAnnotation( 370 int typeRef, TypePath typePath, String desc, boolean visible) { 371 typeReference(desc); 372 return av; 373 } 374 } 375 376 private class PrefixReferenceAnnotationVisitor extends AnnotationVisitor { 377 378 public PrefixReferenceAnnotationVisitor() { 379 super(Opcodes.ASM6); 380 } 381 382 @Override 383 public void visit(String name, Object value) { 384 if (value instanceof Type) { 385 typeReference((Type) value); 386 } 387 } 388 389 @Override 390 public void visitEnum(String name, String desc, String value) { 391 fieldReference(desc.substring(1, desc.length() - 1), value, desc); 392 } 393 394 @Override 395 public AnnotationVisitor visitAnnotation(String name, String desc) { 396 typeReference(desc); 397 return av; 398 } 399 400 @Override 401 public AnnotationVisitor visitArray(String name) { 402 return av; 403 } 404 } 405 } 406