Mercurial > hg > graal-jvmci-8
annotate agent/make/ClosureFinder.java @ 4137:04b9a2566eec
Merge with hsx23/hotspot.
author | Thomas Wuerthinger <thomas.wuerthinger@oracle.com> |
---|---|
date | Sat, 17 Dec 2011 21:40:27 +0100 |
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 } |