• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    */
scan(ClassReader reader, String prefix)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 
PrefixReferenceScanner(String prefix)54   public PrefixReferenceScanner(String prefix) {
55     super(Opcodes.ASM6);
56     this.prefix = prefix;
57   }
58 
59   @Override
visit( int version, int access, String name, String signature, String superName, String[] interfaces)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
visitAnnotation(String desc, boolean visible)76   public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
77     typeReference(desc);
78     return av;
79   }
80 
81   @Override
visitOuterClass(String owner, String name, String desc)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
visitTypeAnnotation( int typeRef, TypePath typePath, String desc, boolean visible)90   public AnnotationVisitor visitTypeAnnotation(
91       int typeRef, TypePath typePath, String desc, boolean visible) {
92     typeReference(desc);
93     return av;
94   }
95 
96   @Override
visitInnerClass(String name, String outerName, String innerName, int access)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
visitField( int access, String name, String desc, String signature, Object value)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
visitMethod( int access, String name, String desc, String signature, String[] exceptions)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 
classReferences(@ullable String[] internalNames)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 
classReference(String internalName)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 
objectReference(String internalName)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 
typeReference(String typeDesc)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 
typeReference(Type type)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 
fieldReference(String owner, String name, String desc)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 
methodReference(String owner, String name, String desc)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 
handleReference(Handle handle)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 
PrefixReferenceMethodVisitor()230     public PrefixReferenceMethodVisitor() {
231       super(Opcodes.ASM6);
232     }
233 
234     @Override
visitAnnotationDefault()235     public AnnotationVisitor visitAnnotationDefault() {
236       return av;
237     }
238 
239     @Override
visitAnnotation(String desc, boolean visible)240     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
241       typeReference(desc);
242       return av;
243     }
244 
245     @Override
visitTypeAnnotation( int typeRef, TypePath typePath, String desc, boolean visible)246     public AnnotationVisitor visitTypeAnnotation(
247         int typeRef, TypePath typePath, String desc, boolean visible) {
248       typeReference(desc);
249       return av;
250     }
251 
252     @Override
visitParameterAnnotation(int parameter, String desc, boolean visible)253     public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
254       typeReference(desc);
255       return av;
256     }
257 
258     @Override
visitTypeInsn(int opcode, String type)259     public void visitTypeInsn(int opcode, String type) {
260       objectReference(type);
261     }
262 
263     @Override
visitFieldInsn(int opcode, String owner, String name, String desc)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
visitMethodInsn(int opcode, String owner, String name, String desc)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
visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)275     public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
276       methodReference(owner, name, desc);
277     }
278 
279     @Override
visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs)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
visitLdcInsn(Object cst)289     public void visitLdcInsn(Object cst) {
290       visitConstant(cst);
291     }
292 
visitConstant(Object cst)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
visitMultiANewArrayInsn(String desc, int dims)311     public void visitMultiANewArrayInsn(String desc, int dims) {
312       typeReference(desc);
313     }
314 
315     @Override
visitInsnAnnotation( int typeRef, TypePath typePath, String desc, boolean visible)316     public AnnotationVisitor visitInsnAnnotation(
317         int typeRef, TypePath typePath, String desc, boolean visible) {
318       typeReference(desc);
319       return av;
320     }
321 
322     @Override
visitTryCatchBlock(Label start, Label end, Label handler, String type)323     public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
324       if (type != null) {
325         classReference(type);
326       }
327     }
328 
329     @Override
visitTryCatchAnnotation( int typeRef, TypePath typePath, String desc, boolean visible)330     public AnnotationVisitor visitTryCatchAnnotation(
331         int typeRef, TypePath typePath, String desc, boolean visible) {
332       typeReference(desc);
333       return av;
334     }
335 
336     @Override
visitLocalVariable( String name, String desc, String signature, Label start, Label end, int index)337     public void visitLocalVariable(
338         String name, String desc, String signature, Label start, Label end, int index) {
339       typeReference(desc);
340     }
341 
342     @Override
visitLocalVariableAnnotation( int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible)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 
PrefixReferenceFieldVisitor()358     public PrefixReferenceFieldVisitor() {
359       super(Opcodes.ASM6);
360     }
361 
362     @Override
visitAnnotation(String desc, boolean visible)363     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
364       typeReference(desc);
365       return av;
366     }
367 
368     @Override
visitTypeAnnotation( int typeRef, TypePath typePath, String desc, boolean visible)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 
PrefixReferenceAnnotationVisitor()378     public PrefixReferenceAnnotationVisitor() {
379       super(Opcodes.ASM6);
380     }
381 
382     @Override
visit(String name, Object value)383     public void visit(String name, Object value) {
384       if (value instanceof Type) {
385         typeReference((Type) value);
386       }
387     }
388 
389     @Override
visitEnum(String name, String desc, String value)390     public void visitEnum(String name, String desc, String value) {
391       fieldReference(desc.substring(1, desc.length() - 1), value, desc);
392     }
393 
394     @Override
visitAnnotation(String name, String desc)395     public AnnotationVisitor visitAnnotation(String name, String desc) {
396       typeReference(desc);
397       return av;
398     }
399 
400     @Override
visitArray(String name)401     public AnnotationVisitor visitArray(String name) {
402       return av;
403     }
404   }
405 }
406