xref: /aosp_15_r20/external/apache-commons-bcel/src/examples/TransitiveHull.java (revision 0c56280ab0842982c46a149f7b9eaa497e31e292)
1*0c56280aSSorin Basca /*
2*0c56280aSSorin Basca  * Licensed to the Apache Software Foundation (ASF) under one or more
3*0c56280aSSorin Basca  * contributor license agreements.  See the NOTICE file distributed with
4*0c56280aSSorin Basca  * this work for additional information regarding copyright ownership.
5*0c56280aSSorin Basca  * The ASF licenses this file to You under the Apache License, Version 2.0
6*0c56280aSSorin Basca  * (the "License"); you may not use this file except in compliance with
7*0c56280aSSorin Basca  * the License.  You may obtain a copy of the License at
8*0c56280aSSorin Basca  *
9*0c56280aSSorin Basca  *      http://www.apache.org/licenses/LICENSE-2.0
10*0c56280aSSorin Basca  *
11*0c56280aSSorin Basca  *  Unless required by applicable law or agreed to in writing, software
12*0c56280aSSorin Basca  *  distributed under the License is distributed on an "AS IS" BASIS,
13*0c56280aSSorin Basca  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*0c56280aSSorin Basca  *  See the License for the specific language governing permissions and
15*0c56280aSSorin Basca  *  limitations under the License.
16*0c56280aSSorin Basca  *
17*0c56280aSSorin Basca  */
18*0c56280aSSorin Basca 
19*0c56280aSSorin Basca import java.util.Arrays;
20*0c56280aSSorin Basca import java.util.regex.Pattern;
21*0c56280aSSorin Basca 
22*0c56280aSSorin Basca import org.apache.bcel.Constants;
23*0c56280aSSorin Basca import org.apache.bcel.Repository;
24*0c56280aSSorin Basca import org.apache.bcel.classfile.ClassParser;
25*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantCP;
26*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantClass;
27*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantFieldref;
28*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantInterfaceMethodref;
29*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantMethodref;
30*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantNameAndType;
31*0c56280aSSorin Basca import org.apache.bcel.classfile.ConstantPool;
32*0c56280aSSorin Basca import org.apache.bcel.classfile.JavaClass;
33*0c56280aSSorin Basca import org.apache.bcel.generic.ArrayType;
34*0c56280aSSorin Basca import org.apache.bcel.generic.ObjectType;
35*0c56280aSSorin Basca import org.apache.bcel.generic.Type;
36*0c56280aSSorin Basca import org.apache.bcel.util.ClassQueue;
37*0c56280aSSorin Basca import org.apache.bcel.util.ClassSet;
38*0c56280aSSorin Basca 
39*0c56280aSSorin Basca /**
40*0c56280aSSorin Basca  * Find all classes referenced by given start class and all classes referenced
41*0c56280aSSorin Basca  * by those and so on. In other words: Compute the transitive hull of classes
42*0c56280aSSorin Basca  * used by a given class. This is done by checking all ConstantClass entries and
43*0c56280aSSorin Basca  * all method and field signatures.<br>
44*0c56280aSSorin Basca  * This may be useful in order to put all class files of an application into a
45*0c56280aSSorin Basca  * single JAR file, e.g..
46*0c56280aSSorin Basca  * <p>
47*0c56280aSSorin Basca  * It fails however in the presence of reflexive code aka introspection.
48*0c56280aSSorin Basca  * <p>
49*0c56280aSSorin Basca  * You'll need Apache's regular expression library supplied together with BCEL
50*0c56280aSSorin Basca  * to use this class.
51*0c56280aSSorin Basca  *
52*0c56280aSSorin Basca  * @version $Id$
53*0c56280aSSorin Basca  */
54*0c56280aSSorin Basca public class TransitiveHull extends org.apache.bcel.classfile.EmptyVisitor {
55*0c56280aSSorin Basca 
56*0c56280aSSorin Basca     private ClassQueue queue;
57*0c56280aSSorin Basca     private ClassSet set;
58*0c56280aSSorin Basca     private ConstantPool cp;
59*0c56280aSSorin Basca     private String[] ignored = IGNORED;
60*0c56280aSSorin Basca 
61*0c56280aSSorin Basca     public static final String[] IGNORED = {"java[.].*", "javax[.].*", "sun[.].*", "sunw[.].*",
62*0c56280aSSorin Basca             "com[.]sun[.].*", "org[.]omg[.].*", "org[.]w3c[.].*", "org[.]xml[.].*", "net[.]jini[.].*"};
63*0c56280aSSorin Basca 
TransitiveHull(JavaClass clazz)64*0c56280aSSorin Basca     public TransitiveHull(JavaClass clazz) {
65*0c56280aSSorin Basca         queue = new ClassQueue();
66*0c56280aSSorin Basca         queue.enqueue(clazz);
67*0c56280aSSorin Basca         set = new ClassSet();
68*0c56280aSSorin Basca         set.add(clazz);
69*0c56280aSSorin Basca     }
70*0c56280aSSorin Basca 
getClasses()71*0c56280aSSorin Basca     public JavaClass[] getClasses() {
72*0c56280aSSorin Basca         return set.toArray();
73*0c56280aSSorin Basca     }
74*0c56280aSSorin Basca 
getClassNames()75*0c56280aSSorin Basca     public String[] getClassNames() {
76*0c56280aSSorin Basca         return set.getClassNames();
77*0c56280aSSorin Basca     }
78*0c56280aSSorin Basca 
79*0c56280aSSorin Basca     /**
80*0c56280aSSorin Basca      * Start traversal using DescendingVisitor pattern.
81*0c56280aSSorin Basca      */
start()82*0c56280aSSorin Basca     public void start() {
83*0c56280aSSorin Basca         while (!queue.empty()) {
84*0c56280aSSorin Basca             JavaClass clazz = queue.dequeue();
85*0c56280aSSorin Basca             cp = clazz.getConstantPool();
86*0c56280aSSorin Basca 
87*0c56280aSSorin Basca             new org.apache.bcel.classfile.DescendingVisitor(clazz, this).visit();
88*0c56280aSSorin Basca         }
89*0c56280aSSorin Basca     }
90*0c56280aSSorin Basca 
add(String class_name)91*0c56280aSSorin Basca     private void add(String class_name) {
92*0c56280aSSorin Basca         class_name = class_name.replace('/', '.');
93*0c56280aSSorin Basca 
94*0c56280aSSorin Basca         for (String anIgnored : ignored) {
95*0c56280aSSorin Basca             if (Pattern.matches(anIgnored, class_name)) {
96*0c56280aSSorin Basca                 return;
97*0c56280aSSorin Basca             }
98*0c56280aSSorin Basca         }
99*0c56280aSSorin Basca 
100*0c56280aSSorin Basca         try {
101*0c56280aSSorin Basca             JavaClass clazz = Repository.lookupClass(class_name);
102*0c56280aSSorin Basca 
103*0c56280aSSorin Basca             if (set.add(clazz)) {
104*0c56280aSSorin Basca                 queue.enqueue(clazz);
105*0c56280aSSorin Basca             }
106*0c56280aSSorin Basca         } catch (ClassNotFoundException e) {
107*0c56280aSSorin Basca             throw new IllegalStateException("Missing class: " + e.toString());
108*0c56280aSSorin Basca         }
109*0c56280aSSorin Basca     }
110*0c56280aSSorin Basca 
111*0c56280aSSorin Basca     @Override
visitConstantClass(ConstantClass cc)112*0c56280aSSorin Basca     public void visitConstantClass(ConstantClass cc) {
113*0c56280aSSorin Basca         String class_name = (String) cc.getConstantValue(cp);
114*0c56280aSSorin Basca         add(class_name);
115*0c56280aSSorin Basca     }
116*0c56280aSSorin Basca 
checkType(Type type)117*0c56280aSSorin Basca     private void checkType(Type type) {
118*0c56280aSSorin Basca         if (type instanceof ArrayType) {
119*0c56280aSSorin Basca             type = ((ArrayType) type).getBasicType();
120*0c56280aSSorin Basca         }
121*0c56280aSSorin Basca 
122*0c56280aSSorin Basca         if (type instanceof ObjectType) {
123*0c56280aSSorin Basca             add(((ObjectType) type).getClassName());
124*0c56280aSSorin Basca         }
125*0c56280aSSorin Basca     }
126*0c56280aSSorin Basca 
visitRef(ConstantCP ccp, boolean method)127*0c56280aSSorin Basca     private void visitRef(ConstantCP ccp, boolean method) {
128*0c56280aSSorin Basca         String class_name = ccp.getClass(cp);
129*0c56280aSSorin Basca         add(class_name);
130*0c56280aSSorin Basca 
131*0c56280aSSorin Basca         ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(ccp.getNameAndTypeIndex(),
132*0c56280aSSorin Basca                 Constants.CONSTANT_NameAndType);
133*0c56280aSSorin Basca 
134*0c56280aSSorin Basca         String signature = cnat.getSignature(cp);
135*0c56280aSSorin Basca 
136*0c56280aSSorin Basca         if (method) {
137*0c56280aSSorin Basca             Type type = Type.getReturnType(signature);
138*0c56280aSSorin Basca 
139*0c56280aSSorin Basca             checkType(type);
140*0c56280aSSorin Basca 
141*0c56280aSSorin Basca             for (Type type1 : Type.getArgumentTypes(signature)) {
142*0c56280aSSorin Basca                 checkType(type1);
143*0c56280aSSorin Basca             }
144*0c56280aSSorin Basca         } else {
145*0c56280aSSorin Basca             checkType(Type.getType(signature));
146*0c56280aSSorin Basca         }
147*0c56280aSSorin Basca     }
148*0c56280aSSorin Basca 
149*0c56280aSSorin Basca     @Override
visitConstantMethodref(ConstantMethodref cmr)150*0c56280aSSorin Basca     public void visitConstantMethodref(ConstantMethodref cmr) {
151*0c56280aSSorin Basca         visitRef(cmr, true);
152*0c56280aSSorin Basca     }
153*0c56280aSSorin Basca 
154*0c56280aSSorin Basca     @Override
visitConstantInterfaceMethodref(ConstantInterfaceMethodref cimr)155*0c56280aSSorin Basca     public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref cimr) {
156*0c56280aSSorin Basca         visitRef(cimr, true);
157*0c56280aSSorin Basca     }
158*0c56280aSSorin Basca 
159*0c56280aSSorin Basca     @Override
visitConstantFieldref(ConstantFieldref cfr)160*0c56280aSSorin Basca     public void visitConstantFieldref(ConstantFieldref cfr) {
161*0c56280aSSorin Basca         visitRef(cfr, false);
162*0c56280aSSorin Basca     }
163*0c56280aSSorin Basca 
getIgnored()164*0c56280aSSorin Basca     public String[] getIgnored() {
165*0c56280aSSorin Basca         return ignored;
166*0c56280aSSorin Basca     }
167*0c56280aSSorin Basca 
168*0c56280aSSorin Basca     /**
169*0c56280aSSorin Basca      * Set the value of ignored.
170*0c56280aSSorin Basca      *
171*0c56280aSSorin Basca      * @param v Value to assign to ignored.
172*0c56280aSSorin Basca      */
setIgnored(String[] v)173*0c56280aSSorin Basca     public void setIgnored(String[] v) {
174*0c56280aSSorin Basca         ignored = v;
175*0c56280aSSorin Basca     }
176*0c56280aSSorin Basca 
main(String[] argv)177*0c56280aSSorin Basca     public static void main(String[] argv) {
178*0c56280aSSorin Basca         JavaClass java_class;
179*0c56280aSSorin Basca 
180*0c56280aSSorin Basca         try {
181*0c56280aSSorin Basca             if (argv.length == 0) {
182*0c56280aSSorin Basca                 System.err.println("transitive: No input files specified");
183*0c56280aSSorin Basca             } else {
184*0c56280aSSorin Basca                 if ((java_class = Repository.lookupClass(argv[0])) == null) {
185*0c56280aSSorin Basca                     java_class = new ClassParser(argv[0]).parse();
186*0c56280aSSorin Basca                 }
187*0c56280aSSorin Basca 
188*0c56280aSSorin Basca                 TransitiveHull hull = new TransitiveHull(java_class);
189*0c56280aSSorin Basca 
190*0c56280aSSorin Basca                 hull.start();
191*0c56280aSSorin Basca                 System.out.println(Arrays.asList(hull.getClassNames()));
192*0c56280aSSorin Basca             }
193*0c56280aSSorin Basca         } catch (Exception e) {
194*0c56280aSSorin Basca             e.printStackTrace();
195*0c56280aSSorin Basca         }
196*0c56280aSSorin Basca     }
197*0c56280aSSorin Basca }
198