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