comparison graal/com.oracle.jvmci.meta/src/com/oracle/jvmci/meta/MetaUtil.java @ 21556:48c1ebd24120

renamed com.oracle.graal.api[meta|code] modules to com.oracle.jvmci.[meta|code] (JBS:GRAAL-53)
author Doug Simon <doug.simon@oracle.com>
date Wed, 27 May 2015 00:36:16 +0200
parents graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java@f137f1974f60
children
comparison
equal deleted inserted replaced
21555:d12eaef9af72 21556:48c1ebd24120
1 /*
2 * Copyright (c) 2012, 2014, 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.
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 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.oracle.jvmci.meta;
24
25 import java.io.*;
26 import java.util.*;
27
28 /**
29 * Miscellaneous collection of utility methods used by {@code com.oracle.jvmci.meta} and its
30 * clients.
31 */
32 public class MetaUtil {
33
34 private static class ClassInfo {
35 public long totalSize;
36 public long instanceCount;
37
38 @Override
39 public String toString() {
40 return "totalSize=" + totalSize + ", instanceCount=" + instanceCount;
41 }
42 }
43
44 /**
45 * Returns the number of bytes occupied by this constant value or constant object and
46 * recursively all values reachable from this value.
47 *
48 * @param constant the constant whose bytes should be measured
49 * @param printTopN print total size and instance count of the top n classes is desired
50 * @return the number of bytes occupied by this constant
51 */
52 public static long getMemorySizeRecursive(MetaAccessProvider access, ConstantReflectionProvider constantReflection, JavaConstant constant, PrintStream out, int printTopN) {
53 Set<JavaConstant> marked = new HashSet<>();
54 Deque<JavaConstant> stack = new ArrayDeque<>();
55 if (constant.getKind() == Kind.Object && constant.isNonNull()) {
56 marked.add(constant);
57 }
58 final HashMap<ResolvedJavaType, ClassInfo> histogram = new HashMap<>();
59 stack.push(constant);
60 long sum = 0;
61 while (!stack.isEmpty()) {
62 JavaConstant c = stack.pop();
63 long memorySize = access.getMemorySize(constant);
64 sum += memorySize;
65 if (c.getKind() == Kind.Object && c.isNonNull()) {
66 ResolvedJavaType clazz = access.lookupJavaType(c);
67 if (!histogram.containsKey(clazz)) {
68 histogram.put(clazz, new ClassInfo());
69 }
70 ClassInfo info = histogram.get(clazz);
71 info.instanceCount++;
72 info.totalSize += memorySize;
73 ResolvedJavaType type = access.lookupJavaType(c);
74 if (type.isArray()) {
75 if (!type.getComponentType().isPrimitive()) {
76 int length = constantReflection.readArrayLength(c);
77 for (int i = 0; i < length; i++) {
78 JavaConstant value = constantReflection.readArrayElement(c, i);
79 pushConstant(marked, stack, value);
80 }
81 }
82 } else {
83 ResolvedJavaField[] instanceFields = type.getInstanceFields(true);
84 for (ResolvedJavaField f : instanceFields) {
85 if (f.getKind() == Kind.Object) {
86 JavaConstant value = constantReflection.readFieldValue(f, c);
87 pushConstant(marked, stack, value);
88 }
89 }
90 }
91 }
92 }
93 ArrayList<ResolvedJavaType> clazzes = new ArrayList<>();
94 clazzes.addAll(histogram.keySet());
95 Collections.sort(clazzes, new Comparator<ResolvedJavaType>() {
96
97 @Override
98 public int compare(ResolvedJavaType o1, ResolvedJavaType o2) {
99 long l1 = histogram.get(o1).totalSize;
100 long l2 = histogram.get(o2).totalSize;
101 if (l1 > l2) {
102 return -1;
103 } else if (l1 == l2) {
104 return 0;
105 } else {
106 return 1;
107 }
108 }
109 });
110
111 int z = 0;
112 for (ResolvedJavaType c : clazzes) {
113 if (z > printTopN) {
114 break;
115 }
116 out.println("Class " + c + ", " + histogram.get(c));
117 ++z;
118 }
119
120 return sum;
121 }
122
123 private static void pushConstant(Set<JavaConstant> marked, Deque<JavaConstant> stack, JavaConstant value) {
124 if (value.isNonNull()) {
125 if (!marked.contains(value)) {
126 marked.add(value);
127 stack.push(value);
128 }
129 }
130 }
131
132 /**
133 * Calls {@link JavaType#resolve(ResolvedJavaType)} on an array of types.
134 */
135 public static ResolvedJavaType[] resolveJavaTypes(JavaType[] types, ResolvedJavaType accessingClass) {
136 ResolvedJavaType[] result = new ResolvedJavaType[types.length];
137 for (int i = 0; i < result.length; i++) {
138 result[i] = types[i].resolve(accessingClass);
139 }
140 return result;
141 }
142
143 /**
144 * Extends the functionality of {@link Class#getSimpleName()} to include a non-empty string for
145 * anonymous and local classes.
146 *
147 * @param clazz the class for which the simple name is being requested
148 * @param withEnclosingClass specifies if the returned name should be qualified with the name(s)
149 * of the enclosing class/classes of {@code clazz} (if any). This option is ignored
150 * if {@code clazz} denotes an anonymous or local class.
151 * @return the simple name
152 */
153 public static String getSimpleName(Class<?> clazz, boolean withEnclosingClass) {
154 final String simpleName = clazz.getSimpleName();
155 if (simpleName.length() != 0) {
156 if (withEnclosingClass) {
157 String prefix = "";
158 Class<?> enclosingClass = clazz;
159 while ((enclosingClass = enclosingClass.getEnclosingClass()) != null) {
160 prefix = enclosingClass.getSimpleName() + "." + prefix;
161 }
162 return prefix + simpleName;
163 }
164 return simpleName;
165 }
166 // Must be an anonymous or local class
167 final String name = clazz.getName();
168 int index = name.indexOf('$');
169 if (index == -1) {
170 return name;
171 }
172 index = name.lastIndexOf('.', index);
173 if (index == -1) {
174 return name;
175 }
176 return name.substring(index + 1);
177 }
178
179 static String internalNameToJava(String name, boolean qualified, boolean classForNameCompatible) {
180 switch (name.charAt(0)) {
181 case 'L': {
182 String result = name.substring(1, name.length() - 1).replace('/', '.');
183 if (!qualified) {
184 final int lastDot = result.lastIndexOf('.');
185 if (lastDot != -1) {
186 result = result.substring(lastDot + 1);
187 }
188 }
189 return result;
190 }
191 case '[':
192 return classForNameCompatible ? name.replace('/', '.') : internalNameToJava(name.substring(1), qualified, classForNameCompatible) + "[]";
193 default:
194 if (name.length() != 1) {
195 throw new IllegalArgumentException("Illegal internal name: " + name);
196 }
197 return Kind.fromPrimitiveOrVoidTypeChar(name.charAt(0)).getJavaName();
198 }
199 }
200
201 /**
202 * Turns an class name in internal format into a resolved Java type.
203 */
204 public static ResolvedJavaType classForName(String internal, MetaAccessProvider metaAccess, ClassLoader cl) {
205 Kind k = Kind.fromTypeString(internal);
206 try {
207 String n = internalNameToJava(internal, true, true);
208 return metaAccess.lookupJavaType(k.isPrimitive() ? k.toJavaClass() : Class.forName(n, true, cl));
209 } catch (ClassNotFoundException cnfe) {
210 throw new IllegalArgumentException("could not instantiate class described by " + internal, cnfe);
211 }
212 }
213
214 /**
215 * Convenient shortcut for calling
216 * {@link #appendLocation(StringBuilder, ResolvedJavaMethod, int)} without having to supply a
217 * {@link StringBuilder} instance and convert the result to a string.
218 */
219 public static String toLocation(ResolvedJavaMethod method, int bci) {
220 return appendLocation(new StringBuilder(), method, bci).toString();
221 }
222
223 /**
224 * Appends a string representation of a location specified by a given method and bci to a given
225 * {@link StringBuilder}. If a stack trace element with a non-null file name and non-negative
226 * line number is {@linkplain ResolvedJavaMethod#asStackTraceElement(int) available} for the
227 * given method, then the string returned is the {@link StackTraceElement#toString()} value of
228 * the stack trace element, suffixed by the bci location. For example:
229 *
230 * <pre>
231 * java.lang.String.valueOf(String.java:2930) [bci: 12]
232 * </pre>
233 *
234 * Otherwise, the string returned is the value of applying {@link JavaMethod#format(String)}
235 * with the format string {@code "%H.%n(%p)"}, suffixed by the bci location. For example:
236 *
237 * <pre>
238 * java.lang.String.valueOf(int) [bci: 12]
239 * </pre>
240 *
241 * @param sb
242 * @param method
243 * @param bci
244 */
245 public static StringBuilder appendLocation(StringBuilder sb, ResolvedJavaMethod method, int bci) {
246 if (method != null) {
247 StackTraceElement ste = method.asStackTraceElement(bci);
248 if (ste.getFileName() != null && ste.getLineNumber() > 0) {
249 sb.append(ste);
250 } else {
251 sb.append(method.format("%H.%n(%p)"));
252 }
253 } else {
254 sb.append("Null method");
255 }
256 return sb.append(" [bci: ").append(bci).append(']');
257 }
258
259 static void appendProfile(StringBuilder buf, AbstractJavaProfile<?, ?> profile, int bci, String type, String sep) {
260 if (profile != null) {
261 AbstractProfiledItem<?>[] pitems = profile.getItems();
262 if (pitems != null) {
263 buf.append(String.format("%s@%d:", type, bci));
264 for (int j = 0; j < pitems.length; j++) {
265 AbstractProfiledItem<?> pitem = pitems[j];
266 buf.append(String.format(" %.6f (%s)%s", pitem.getProbability(), pitem.getItem(), sep));
267 }
268 if (profile.getNotRecordedProbability() != 0) {
269 buf.append(String.format(" %.6f <other %s>%s", profile.getNotRecordedProbability(), type, sep));
270 } else {
271 buf.append(String.format(" <no other %s>%s", type, sep));
272 }
273 }
274 }
275 }
276
277 /**
278 * Converts a Java source-language class name into the internal form.
279 *
280 * @param className the class name
281 * @return the internal name form of the class name
282 */
283 public static String toInternalName(String className) {
284 String prefix = "";
285 String base = className;
286 while (base.endsWith("[]")) {
287 prefix += "[";
288 base = base.substring(base.length() - 2);
289 }
290
291 switch (className) {
292 case "boolean":
293 return prefix + "Z";
294 case "byte":
295 return prefix + "B";
296 case "short":
297 return prefix + "S";
298 case "char":
299 return prefix + "C";
300 case "int":
301 return prefix + "I";
302 case "float":
303 return prefix + "F";
304 case "long":
305 return prefix + "J";
306 case "double":
307 return prefix + "D";
308 case "void":
309 return prefix + "V";
310 default:
311 return prefix + "L" + className.replace('.', '/') + ";";
312 }
313 }
314
315 /**
316 * Prepends the String {@code indentation} to every line in String {@code lines}, including a
317 * possibly non-empty line following the final newline.
318 */
319 public static String indent(String lines, String indentation) {
320 if (lines.length() == 0) {
321 return lines;
322 }
323 final String newLine = "\n";
324 if (lines.endsWith(newLine)) {
325 return indentation + (lines.substring(0, lines.length() - 1)).replace(newLine, newLine + indentation) + newLine;
326 }
327 return indentation + lines.replace(newLine, newLine + indentation);
328 }
329
330 /**
331 * Gets a string representation of an object based soley on its class and its
332 * {@linkplain System#identityHashCode(Object) identity hash code}. This avoids and calls to
333 * virtual methods on the object such as {@link Object#hashCode()}.
334 */
335 public static String identityHashCodeString(Object obj) {
336 if (obj == null) {
337 return "null";
338 }
339 return obj.getClass().getName() + "@" + System.identityHashCode(obj);
340 }
341 }