src/share/classes/com/sun/tools/classfile/Dependencies.java

changeset 0
959103a6100f
child 2525
2eb010b6cb22
equal deleted inserted replaced
-1:000000000000 0:959103a6100f
1 /*
2 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.sun.tools.classfile;
26
27 import java.util.Deque;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.regex.Pattern;
35
36 import com.sun.tools.classfile.Dependency.Filter;
37 import com.sun.tools.classfile.Dependency.Finder;
38 import com.sun.tools.classfile.Dependency.Location;
39 import com.sun.tools.classfile.Type.ArrayType;
40 import com.sun.tools.classfile.Type.ClassSigType;
41 import com.sun.tools.classfile.Type.ClassType;
42 import com.sun.tools.classfile.Type.MethodType;
43 import com.sun.tools.classfile.Type.SimpleType;
44 import com.sun.tools.classfile.Type.TypeParamType;
45 import com.sun.tools.classfile.Type.WildcardType;
46 import static com.sun.tools.classfile.ConstantPool.*;
47
48 /**
49 * A framework for determining {@link Dependency dependencies} between class files.
50 *
51 * A {@link Dependency.Finder finder} is used to identify the dependencies of
52 * individual classes. Some finders may return subtypes of {@code Dependency} to
53 * further characterize the type of dependency, such as a dependency on a
54 * method within a class.
55 *
56 * A {@link Dependency.Filter filter} may be used to restrict the set of
57 * dependencies found by a finder.
58 *
59 * Dependencies that are found may be passed to a {@link Dependencies.Recorder
60 * recorder} so that the dependencies can be stored in a custom data structure.
61 */
62 public class Dependencies {
63 /**
64 * Thrown when a class file cannot be found.
65 */
66 public static class ClassFileNotFoundException extends Exception {
67 private static final long serialVersionUID = 3632265927794475048L;
68
69 public ClassFileNotFoundException(String className) {
70 super(className);
71 this.className = className;
72 }
73
74 public ClassFileNotFoundException(String className, Throwable cause) {
75 this(className);
76 initCause(cause);
77 }
78
79 public final String className;
80 }
81
82 /**
83 * Thrown when an exception is found processing a class file.
84 */
85 public static class ClassFileError extends Error {
86 private static final long serialVersionUID = 4111110813961313203L;
87
88 public ClassFileError(Throwable cause) {
89 initCause(cause);
90 }
91 }
92
93 /**
94 * Service provider interface to locate and read class files.
95 */
96 public interface ClassFileReader {
97 /**
98 * Get the ClassFile object for a specified class.
99 * @param className the name of the class to be returned.
100 * @return the ClassFile for the given class
101 * @throws Dependencies.ClassFileNotFoundException if the classfile cannot be
102 * found
103 */
104 public ClassFile getClassFile(String className)
105 throws ClassFileNotFoundException;
106 }
107
108 /**
109 * Service provide interface to handle results.
110 */
111 public interface Recorder {
112 /**
113 * Record a dependency that has been found.
114 * @param d
115 */
116 public void addDependency(Dependency d);
117 }
118
119 /**
120 * Get the default finder used to locate the dependencies for a class.
121 * @return the default finder
122 */
123 public static Finder getDefaultFinder() {
124 return new APIDependencyFinder(AccessFlags.ACC_PRIVATE);
125 }
126
127 /**
128 * Get a finder used to locate the API dependencies for a class.
129 * These include the superclass, superinterfaces, and classes referenced in
130 * the declarations of fields and methods. The fields and methods that
131 * are checked can be limited according to a specified access.
132 * The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC},
133 * {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE},
134 * {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for
135 * package private access. Members with greater than or equal accessibility
136 * to that specified will be searched for dependencies.
137 * @param access the access of members to be checked
138 * @return an API finder
139 */
140 public static Finder getAPIFinder(int access) {
141 return new APIDependencyFinder(access);
142 }
143
144 /**
145 * Get a finder to do class dependency analysis.
146 *
147 * @return a Class dependency finder
148 */
149 public static Finder getClassDependencyFinder() {
150 return new ClassDependencyFinder();
151 }
152
153 /**
154 * Get the finder used to locate the dependencies for a class.
155 * @return the finder
156 */
157 public Finder getFinder() {
158 if (finder == null)
159 finder = getDefaultFinder();
160 return finder;
161 }
162
163 /**
164 * Set the finder used to locate the dependencies for a class.
165 * @param f the finder
166 */
167 public void setFinder(Finder f) {
168 f.getClass(); // null check
169 finder = f;
170 }
171
172 /**
173 * Get the default filter used to determine included when searching
174 * the transitive closure of all the dependencies.
175 * Unless overridden, the default filter accepts all dependencies.
176 * @return the default filter.
177 */
178 public static Filter getDefaultFilter() {
179 return DefaultFilter.instance();
180 }
181
182 /**
183 * Get a filter which uses a regular expression on the target's class name
184 * to determine if a dependency is of interest.
185 * @param pattern the pattern used to match the target's class name
186 * @return a filter for matching the target class name with a regular expression
187 */
188 public static Filter getRegexFilter(Pattern pattern) {
189 return new TargetRegexFilter(pattern);
190 }
191
192 /**
193 * Get a filter which checks the package of a target's class name
194 * to determine if a dependency is of interest. The filter checks if the
195 * package of the target's class matches any of a set of given package
196 * names. The match may optionally match subpackages of the given names as well.
197 * @param packageNames the package names used to match the target's class name
198 * @param matchSubpackages whether or not to match subpackages as well
199 * @return a filter for checking the target package name against a list of package names
200 */
201 public static Filter getPackageFilter(Set<String> packageNames, boolean matchSubpackages) {
202 return new TargetPackageFilter(packageNames, matchSubpackages);
203 }
204
205 /**
206 * Get the filter used to determine the dependencies included when searching
207 * the transitive closure of all the dependencies.
208 * Unless overridden, the default filter accepts all dependencies.
209 * @return the filter
210 */
211 public Filter getFilter() {
212 if (filter == null)
213 filter = getDefaultFilter();
214 return filter;
215 }
216
217 /**
218 * Set the filter used to determine the dependencies included when searching
219 * the transitive closure of all the dependencies.
220 * @param f the filter
221 */
222 public void setFilter(Filter f) {
223 f.getClass(); // null check
224 filter = f;
225 }
226
227 /**
228 * Find the dependencies of a class, using the current
229 * {@link Dependencies#getFinder finder} and
230 * {@link Dependencies#getFilter filter}.
231 * The search may optionally include the transitive closure of all the
232 * filtered dependencies, by also searching in the classes named in those
233 * dependencies.
234 * @param classFinder a finder to locate class files
235 * @param rootClassNames the names of the root classes from which to begin
236 * searching
237 * @param transitiveClosure whether or not to also search those classes
238 * named in any filtered dependencies that are found.
239 * @return the set of dependencies that were found
240 * @throws ClassFileNotFoundException if a required class file cannot be found
241 * @throws ClassFileError if an error occurs while processing a class file,
242 * such as an error in the internal class file structure.
243 */
244 public Set<Dependency> findAllDependencies(
245 ClassFileReader classFinder, Set<String> rootClassNames,
246 boolean transitiveClosure)
247 throws ClassFileNotFoundException {
248 final Set<Dependency> results = new HashSet<Dependency>();
249 Recorder r = new Recorder() {
250 public void addDependency(Dependency d) {
251 results.add(d);
252 }
253 };
254 findAllDependencies(classFinder, rootClassNames, transitiveClosure, r);
255 return results;
256 }
257
258 /**
259 * Find the dependencies of a class, using the current
260 * {@link Dependencies#getFinder finder} and
261 * {@link Dependencies#getFilter filter}.
262 * The search may optionally include the transitive closure of all the
263 * filtered dependencies, by also searching in the classes named in those
264 * dependencies.
265 * @param classFinder a finder to locate class files
266 * @param rootClassNames the names of the root classes from which to begin
267 * searching
268 * @param transitiveClosure whether or not to also search those classes
269 * named in any filtered dependencies that are found.
270 * @param recorder a recorder for handling the results
271 * @throws ClassFileNotFoundException if a required class file cannot be found
272 * @throws ClassFileError if an error occurs while processing a class file,
273 * such as an error in the internal class file structure.
274 */
275 public void findAllDependencies(
276 ClassFileReader classFinder, Set<String> rootClassNames,
277 boolean transitiveClosure, Recorder recorder)
278 throws ClassFileNotFoundException {
279 Set<String> doneClasses = new HashSet<String>();
280
281 getFinder(); // ensure initialized
282 getFilter(); // ensure initialized
283
284 // Work queue of names of classfiles to be searched.
285 // Entries will be unique, and for classes that do not yet have
286 // dependencies in the results map.
287 Deque<String> deque = new LinkedList<String>(rootClassNames);
288
289 String className;
290 while ((className = deque.poll()) != null) {
291 assert (!doneClasses.contains(className));
292 doneClasses.add(className);
293
294 ClassFile cf = classFinder.getClassFile(className);
295
296 // The following code just applies the filter to the dependencies
297 // followed for the transitive closure.
298 for (Dependency d: finder.findDependencies(cf)) {
299 recorder.addDependency(d);
300 if (transitiveClosure && filter.accepts(d)) {
301 String cn = d.getTarget().getClassName();
302 if (!doneClasses.contains(cn))
303 deque.add(cn);
304 }
305 }
306 }
307 }
308
309 private Filter filter;
310 private Finder finder;
311
312 /**
313 * A location identifying a class.
314 */
315 static class SimpleLocation implements Location {
316 public SimpleLocation(String name) {
317 this.name = name;
318 this.className = name.replace('/', '.');
319 }
320
321 public String getName() {
322 return name;
323 }
324
325 public String getClassName() {
326 return className;
327 }
328
329 public String getPackageName() {
330 int i = name.lastIndexOf('/');
331 return (i > 0) ? name.substring(0, i).replace('/', '.') : "";
332 }
333
334 @Override
335 public boolean equals(Object other) {
336 if (this == other)
337 return true;
338 if (!(other instanceof SimpleLocation))
339 return false;
340 return (name.equals(((SimpleLocation) other).name));
341 }
342
343 @Override
344 public int hashCode() {
345 return name.hashCode();
346 }
347
348 @Override
349 public String toString() {
350 return name;
351 }
352
353 private String name;
354 private String className;
355 }
356
357 /**
358 * A dependency of one class on another.
359 */
360 static class SimpleDependency implements Dependency {
361 public SimpleDependency(Location origin, Location target) {
362 this.origin = origin;
363 this.target = target;
364 }
365
366 public Location getOrigin() {
367 return origin;
368 }
369
370 public Location getTarget() {
371 return target;
372 }
373
374 @Override
375 public boolean equals(Object other) {
376 if (this == other)
377 return true;
378 if (!(other instanceof SimpleDependency))
379 return false;
380 SimpleDependency o = (SimpleDependency) other;
381 return (origin.equals(o.origin) && target.equals(o.target));
382 }
383
384 @Override
385 public int hashCode() {
386 return origin.hashCode() * 31 + target.hashCode();
387 }
388
389 @Override
390 public String toString() {
391 return origin + ":" + target;
392 }
393
394 private Location origin;
395 private Location target;
396 }
397
398
399 /**
400 * This class accepts all dependencies.
401 */
402 static class DefaultFilter implements Filter {
403 private static DefaultFilter instance;
404
405 static DefaultFilter instance() {
406 if (instance == null)
407 instance = new DefaultFilter();
408 return instance;
409 }
410
411 public boolean accepts(Dependency dependency) {
412 return true;
413 }
414 }
415
416 /**
417 * This class accepts those dependencies whose target's class name matches a
418 * regular expression.
419 */
420 static class TargetRegexFilter implements Filter {
421 TargetRegexFilter(Pattern pattern) {
422 this.pattern = pattern;
423 }
424
425 public boolean accepts(Dependency dependency) {
426 return pattern.matcher(dependency.getTarget().getClassName()).matches();
427 }
428
429 private final Pattern pattern;
430 }
431
432 /**
433 * This class accepts those dependencies whose class name is in a given
434 * package.
435 */
436 static class TargetPackageFilter implements Filter {
437 TargetPackageFilter(Set<String> packageNames, boolean matchSubpackages) {
438 for (String pn: packageNames) {
439 if (pn.length() == 0) // implies null check as well
440 throw new IllegalArgumentException();
441 }
442 this.packageNames = packageNames;
443 this.matchSubpackages = matchSubpackages;
444 }
445
446 public boolean accepts(Dependency dependency) {
447 String pn = dependency.getTarget().getPackageName();
448 if (packageNames.contains(pn))
449 return true;
450
451 if (matchSubpackages) {
452 for (String n: packageNames) {
453 if (pn.startsWith(n + "."))
454 return true;
455 }
456 }
457
458 return false;
459 }
460
461 private final Set<String> packageNames;
462 private final boolean matchSubpackages;
463 }
464
465 /**
466 * This class identifies class names directly or indirectly in the constant pool.
467 */
468 static class ClassDependencyFinder extends BasicDependencyFinder {
469 public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
470 Visitor v = new Visitor(classfile);
471 for (CPInfo cpInfo: classfile.constant_pool.entries()) {
472 v.scan(cpInfo);
473 }
474 try {
475 v.addClass(classfile.super_class);
476 v.addClasses(classfile.interfaces);
477 v.scan(classfile.attributes);
478
479 for (Field f : classfile.fields) {
480 v.scan(f.descriptor, f.attributes);
481 }
482 for (Method m : classfile.methods) {
483 v.scan(m.descriptor, m.attributes);
484 Exceptions_attribute e =
485 (Exceptions_attribute)m.attributes.get(Attribute.Exceptions);
486 if (e != null) {
487 v.addClasses(e.exception_index_table);
488 }
489 }
490 } catch (ConstantPoolException e) {
491 throw new ClassFileError(e);
492 }
493
494 return v.deps;
495 }
496 }
497
498 /**
499 * This class identifies class names in the signatures of classes, fields,
500 * and methods in a class.
501 */
502 static class APIDependencyFinder extends BasicDependencyFinder {
503 APIDependencyFinder(int access) {
504 switch (access) {
505 case AccessFlags.ACC_PUBLIC:
506 case AccessFlags.ACC_PROTECTED:
507 case AccessFlags.ACC_PRIVATE:
508 case 0:
509 showAccess = access;
510 break;
511 default:
512 throw new IllegalArgumentException("invalid access 0x"
513 + Integer.toHexString(access));
514 }
515 }
516
517 public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
518 try {
519 Visitor v = new Visitor(classfile);
520 v.addClass(classfile.super_class);
521 v.addClasses(classfile.interfaces);
522 // inner classes?
523 for (Field f : classfile.fields) {
524 if (checkAccess(f.access_flags))
525 v.scan(f.descriptor, f.attributes);
526 }
527 for (Method m : classfile.methods) {
528 if (checkAccess(m.access_flags)) {
529 v.scan(m.descriptor, m.attributes);
530 Exceptions_attribute e =
531 (Exceptions_attribute) m.attributes.get(Attribute.Exceptions);
532 if (e != null)
533 v.addClasses(e.exception_index_table);
534 }
535 }
536 return v.deps;
537 } catch (ConstantPoolException e) {
538 throw new ClassFileError(e);
539 }
540 }
541
542 boolean checkAccess(AccessFlags flags) {
543 // code copied from javap.Options.checkAccess
544 boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC);
545 boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED);
546 boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE);
547 boolean isPackage = !(isPublic || isProtected || isPrivate);
548
549 if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
550 return false;
551 else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage))
552 return false;
553 else if ((showAccess == 0) && (isPrivate))
554 return false;
555 else
556 return true;
557 }
558
559 private int showAccess;
560 }
561
562 static abstract class BasicDependencyFinder implements Finder {
563 private Map<String,Location> locations = new HashMap<String,Location>();
564
565 Location getLocation(String className) {
566 Location l = locations.get(className);
567 if (l == null)
568 locations.put(className, l = new SimpleLocation(className));
569 return l;
570 }
571
572 class Visitor implements ConstantPool.Visitor<Void,Void>, Type.Visitor<Void, Void> {
573 private ConstantPool constant_pool;
574 private Location origin;
575 Set<Dependency> deps;
576
577 Visitor(ClassFile classFile) {
578 try {
579 constant_pool = classFile.constant_pool;
580 origin = getLocation(classFile.getName());
581 deps = new HashSet<Dependency>();
582 } catch (ConstantPoolException e) {
583 throw new ClassFileError(e);
584 }
585 }
586
587 void scan(Descriptor d, Attributes attrs) {
588 try {
589 scan(new Signature(d.index).getType(constant_pool));
590 scan(attrs);
591 } catch (ConstantPoolException e) {
592 throw new ClassFileError(e);
593 }
594 }
595
596 void scan(CPInfo cpInfo) {
597 cpInfo.accept(this, null);
598 }
599
600 void scan(Type t) {
601 t.accept(this, null);
602 }
603
604 void scan(Attributes attrs) {
605 try {
606 Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);
607 if (sa != null)
608 scan(sa.getParsedSignature().getType(constant_pool));
609
610 scan((RuntimeVisibleAnnotations_attribute)
611 attrs.get(Attribute.RuntimeVisibleAnnotations));
612 scan((RuntimeVisibleParameterAnnotations_attribute)
613 attrs.get(Attribute.RuntimeVisibleParameterAnnotations));
614 } catch (ConstantPoolException e) {
615 throw new ClassFileError(e);
616 }
617 }
618
619 private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {
620 if (attr == null) {
621 return;
622 }
623 for (int i = 0; i < attr.annotations.length; i++) {
624 int index = attr.annotations[i].type_index;
625 scan(new Signature(index).getType(constant_pool));
626 }
627 }
628
629 private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {
630 if (attr == null) {
631 return;
632 }
633 for (int param = 0; param < attr.parameter_annotations.length; param++) {
634 for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
635 int index = attr.parameter_annotations[param][i].type_index;
636 scan(new Signature(index).getType(constant_pool));
637 }
638 }
639 }
640
641 void addClass(int index) throws ConstantPoolException {
642 if (index != 0) {
643 String name = constant_pool.getClassInfo(index).getBaseName();
644 if (name != null)
645 addDependency(name);
646 }
647 }
648
649 void addClasses(int[] indices) throws ConstantPoolException {
650 for (int i: indices)
651 addClass(i);
652 }
653
654 private void addDependency(String name) {
655 deps.add(new SimpleDependency(origin, getLocation(name)));
656 }
657
658 // ConstantPool.Visitor methods
659
660 public Void visitClass(CONSTANT_Class_info info, Void p) {
661 try {
662 if (info.getName().startsWith("["))
663 new Signature(info.name_index).getType(constant_pool).accept(this, null);
664 else
665 addDependency(info.getBaseName());
666 return null;
667 } catch (ConstantPoolException e) {
668 throw new ClassFileError(e);
669 }
670 }
671
672 public Void visitDouble(CONSTANT_Double_info info, Void p) {
673 return null;
674 }
675
676 public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) {
677 return visitRef(info, p);
678 }
679
680 public Void visitFloat(CONSTANT_Float_info info, Void p) {
681 return null;
682 }
683
684 public Void visitInteger(CONSTANT_Integer_info info, Void p) {
685 return null;
686 }
687
688 public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) {
689 return visitRef(info, p);
690 }
691
692 public Void visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) {
693 return null;
694 }
695
696 public Void visitLong(CONSTANT_Long_info info, Void p) {
697 return null;
698 }
699
700 public Void visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) {
701 return null;
702 }
703
704 public Void visitMethodType(CONSTANT_MethodType_info info, Void p) {
705 return null;
706 }
707
708 public Void visitMethodref(CONSTANT_Methodref_info info, Void p) {
709 return visitRef(info, p);
710 }
711
712 public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) {
713 try {
714 new Signature(info.type_index).getType(constant_pool).accept(this, null);
715 return null;
716 } catch (ConstantPoolException e) {
717 throw new ClassFileError(e);
718 }
719 }
720
721 public Void visitString(CONSTANT_String_info info, Void p) {
722 return null;
723 }
724
725 public Void visitUtf8(CONSTANT_Utf8_info info, Void p) {
726 return null;
727 }
728
729 private Void visitRef(CPRefInfo info, Void p) {
730 try {
731 visitClass(info.getClassInfo(), p);
732 return null;
733 } catch (ConstantPoolException e) {
734 throw new ClassFileError(e);
735 }
736 }
737
738 // Type.Visitor methods
739
740 private void findDependencies(Type t) {
741 if (t != null)
742 t.accept(this, null);
743 }
744
745 private void findDependencies(List<? extends Type> ts) {
746 if (ts != null) {
747 for (Type t: ts)
748 t.accept(this, null);
749 }
750 }
751
752 public Void visitSimpleType(SimpleType type, Void p) {
753 return null;
754 }
755
756 public Void visitArrayType(ArrayType type, Void p) {
757 findDependencies(type.elemType);
758 return null;
759 }
760
761 public Void visitMethodType(MethodType type, Void p) {
762 findDependencies(type.paramTypes);
763 findDependencies(type.returnType);
764 findDependencies(type.throwsTypes);
765 findDependencies(type.typeParamTypes);
766 return null;
767 }
768
769 public Void visitClassSigType(ClassSigType type, Void p) {
770 findDependencies(type.superclassType);
771 findDependencies(type.superinterfaceTypes);
772 return null;
773 }
774
775 public Void visitClassType(ClassType type, Void p) {
776 findDependencies(type.outerType);
777 addDependency(type.getBinaryName());
778 findDependencies(type.typeArgs);
779 return null;
780 }
781
782 public Void visitTypeParamType(TypeParamType type, Void p) {
783 findDependencies(type.classBound);
784 findDependencies(type.interfaceBounds);
785 return null;
786 }
787
788 public Void visitWildcardType(WildcardType type, Void p) {
789 findDependencies(type.boundType);
790 return null;
791 }
792 }
793 }
794 }

mercurial