Mercurial > hg > truffle
annotate agent/make/ClosureFinder.java @ 3237:399aa66d375e
Fixed a bug in which the valueEquals method was misused. The method does only check the equality of the node data and not full GVN equality by taking inputs and successors into account.
author | Thomas Wuerthinger <thomas@wuerthinger.net> |
---|---|
date | Wed, 27 Jul 2011 14:16:38 -0700 |
parents | c18cbe5936b8 |
children |
rev | line source |
---|---|
0 | 1 /* |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
2 * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. |
0 | 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. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
0
diff
changeset
|
21 * questions. |
0 | 22 * |
23 */ | |
24 | |
25 import java.io.*; | |
26 import java.util.*; | |
27 | |
28 | |
29 /** | |
30 <p> This class finds transitive closure of dependencies from a given | |
31 root set of classes. If your project has lots of .class files and you | |
32 want to ship only those .class files which are used (transitively) | |
33 from a root set of classes, then you can use this utility. </p> <p> | |
34 How does it work?</p> | |
35 | |
36 <p> We walk through all constant pool entries of a given class and | |
37 find all modified UTF-8 entries. Anything that looks like a class name is | |
38 considered as a class and we search for that class in the given | |
39 classpath. If we find a .class of that name, then we add that class to | |
40 list.</p> | |
41 | |
42 <p> We could have used CONSTANT_ClassInfo type constants only. But | |
43 that will miss classes used through Class.forName or xyz.class | |
44 construct. But, if you refer to a class name in some other string we | |
45 would include it as dependency :(. But this is quite unlikely | |
46 anyway. To look for exact Class.forName argument(s) would involve | |
47 bytecode analysis. Also, we handle only simple reflection. If you | |
48 accept name of a class from externally (for eg properties file or | |
49 command line args for example, this utility will not be able to find | |
50 that dependency. In such cases, include those classes in the root set. | |
51 </p> | |
52 */ | |
53 | |
54 public class ClosureFinder { | |
55 private Collection roots; // root class names Collection<String> | |
56 private Map visitedClasses; // set of all dependencies as a Map | |
57 private String classPath; // classpath to look for .class files | |
58 private String[] pathComponents; // classpath components | |
59 private static final boolean isWindows = File.separatorChar != '/'; | |
60 | |
61 public ClosureFinder(Collection roots, String classPath) { | |
62 this.roots = roots; | |
63 this.classPath = classPath; | |
64 parseClassPath(); | |
65 } | |
66 | |
67 // parse classPath into pathComponents array | |
68 private void parseClassPath() { | |
69 List paths = new ArrayList(); | |
70 StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator); | |
71 while (st.hasMoreTokens()) | |
72 paths.add(st.nextToken()); | |
73 | |
74 Object[] arr = paths.toArray(); | |
75 pathComponents = new String[arr.length]; | |
76 System.arraycopy(arr, 0, pathComponents, 0, arr.length); | |
77 } | |
78 | |
79 // if output is aleady not computed, compute it now | |
80 // result is a map from class file name to base path where the .class was found | |
81 public Map find() { | |
82 if (visitedClasses == null) { | |
83 visitedClasses = new HashMap(); | |
84 computeClosure(); | |
85 } | |
86 return visitedClasses; | |
87 } | |
88 | |
89 // compute closure for all given root classes | |
90 private void computeClosure() { | |
91 for (Iterator rootsItr = roots.iterator(); rootsItr.hasNext();) { | |
92 String name = (String) rootsItr.next(); | |
93 name = name.substring(0, name.indexOf(".class")); | |
94 computeClosure(name); | |
95 } | |
96 } | |
97 | |
98 | |
99 // looks up for .class in pathComponents and returns | |
100 // base path if found, else returns null | |
101 private String lookupClassFile(String classNameAsPath) { | |
102 for (int i = 0; i < pathComponents.length; i++) { | |
103 File f = new File(pathComponents[i] + File.separator + | |
104 classNameAsPath + ".class"); | |
105 if (f.exists()) { | |
106 if (isWindows) { | |
107 String name = f.getName(); | |
108 // Windows reports special devices AUX,NUL,CON as files | |
109 // under any directory. It does not care about file extention :-( | |
110 if (name.compareToIgnoreCase("AUX.class") == 0 || | |
111 name.compareToIgnoreCase("NUL.class") == 0 || | |
112 name.compareToIgnoreCase("CON.class") == 0) { | |
113 return null; | |
114 } | |
115 } | |
116 return pathComponents[i]; | |
117 } | |
118 } | |
119 return null; | |
120 } | |
121 | |
122 | |
123 // from JVM spec. 2'nd edition section 4.4 | |
124 private static final int CONSTANT_Class = 7; | |
125 private static final int CONSTANT_FieldRef = 9; | |
126 private static final int CONSTANT_MethodRef = 10; | |
127 private static final int CONSTANT_InterfaceMethodRef = 11; | |
128 private static final int CONSTANT_String = 8; | |
129 private static final int CONSTANT_Integer = 3; | |
130 private static final int CONSTANT_Float = 4; | |
131 private static final int CONSTANT_Long = 5; | |
132 private static final int CONSTANT_Double = 6; | |
133 private static final int CONSTANT_NameAndType = 12; | |
134 private static final int CONSTANT_Utf8 = 1; | |
135 | |
136 // whether a given string may be a class name? | |
137 private boolean mayBeClassName(String internalClassName) { | |
138 int len = internalClassName.length(); | |
139 for (int s = 0; s < len; s++) { | |
140 char c = internalClassName.charAt(s); | |
141 if (!Character.isJavaIdentifierPart(c) && c != '/') | |
142 return false; | |
143 } | |
144 return true; | |
145 } | |
146 | |
147 // compute closure for a given class | |
148 private void computeClosure(String className) { | |
149 if (visitedClasses.get(className) != null) return; | |
150 String basePath = lookupClassFile(className); | |
151 if (basePath != null) { | |
152 visitedClasses.put(className, basePath); | |
153 try { | |
154 File classFile = new File(basePath + File.separator + className + ".class"); | |
155 FileInputStream fis = new FileInputStream(classFile); | |
156 DataInputStream dis = new DataInputStream(fis); | |
157 // look for .class signature | |
158 if (dis.readInt() != 0xcafebabe) { | |
159 System.err.println(classFile.getAbsolutePath() + " is not a valid .class file"); | |
160 return; | |
161 } | |
162 | |
163 // ignore major and minor version numbers | |
164 dis.readShort(); | |
165 dis.readShort(); | |
166 | |
167 // read number of constant pool constants | |
168 int numConsts = (int) dis.readShort(); | |
169 String[] strings = new String[numConsts]; | |
170 | |
171 // zero'th entry is unused | |
172 for (int cpIndex = 1; cpIndex < numConsts; cpIndex++) { | |
173 int constType = (int) dis.readByte(); | |
174 switch (constType) { | |
175 case CONSTANT_Class: | |
176 case CONSTANT_String: | |
177 dis.readShort(); // string name index; | |
178 break; | |
179 | |
180 case CONSTANT_FieldRef: | |
181 case CONSTANT_MethodRef: | |
182 case CONSTANT_InterfaceMethodRef: | |
183 case CONSTANT_NameAndType: | |
184 case CONSTANT_Integer: | |
185 case CONSTANT_Float: | |
186 // all these are 4 byte constants | |
187 dis.readInt(); | |
188 break; | |
189 | |
190 case CONSTANT_Long: | |
191 case CONSTANT_Double: | |
192 // 8 byte constants | |
193 dis.readLong(); | |
194 // occupies 2 cp entries | |
195 cpIndex++; | |
196 break; | |
197 | |
198 | |
199 case CONSTANT_Utf8: { | |
200 strings[cpIndex] = dis.readUTF(); | |
201 break; | |
202 } | |
203 | |
204 default: | |
205 System.err.println("invalid constant pool entry"); | |
206 return; | |
207 } | |
208 } | |
209 | |
210 // now walk thru the string constants and look for class names | |
211 for (int s = 0; s < numConsts; s++) { | |
212 if (strings[s] != null && mayBeClassName(strings[s])) | |
213 computeClosure(strings[s].replace('/', File.separatorChar)); | |
214 } | |
215 | |
216 } catch (IOException exp) { | |
217 // ignore for now | |
218 } | |
219 | |
220 } | |
221 } | |
222 | |
223 // a sample main that accepts roots classes in a file and classpath as args | |
224 public static void main(String[] args) { | |
225 if (args.length != 2) { | |
226 System.err.println("Usage: ClosureFinder <root class file> <class path>"); | |
227 System.exit(1); | |
228 } | |
229 | |
230 List roots = new ArrayList(); | |
231 try { | |
232 FileInputStream fis = new FileInputStream(args[0]); | |
233 DataInputStream dis = new DataInputStream(fis); | |
234 String line = null; | |
235 while ((line = dis.readLine()) != null) { | |
236 if (isWindows) { | |
237 line = line.replace('/', File.separatorChar); | |
238 } | |
239 roots.add(line); | |
240 } | |
241 } catch (IOException exp) { | |
242 System.err.println(exp.getMessage()); | |
243 System.exit(2); | |
244 } | |
245 | |
246 ClosureFinder cf = new ClosureFinder(roots, args[1]); | |
247 Map out = cf.find(); | |
248 Iterator res = out.keySet().iterator(); | |
249 for(; res.hasNext(); ) { | |
250 String className = (String) res.next(); | |
251 System.out.println(className + ".class"); | |
252 } | |
253 } | |
254 } |