1 /* 2 * Copyright (c) 2012, 2019, 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 jdk.vm.ci.meta; 24 25 /** 26 * Miscellaneous collection of utility methods used by {@code jdk.vm.ci.meta} and its clients. 27 */ 28 public class MetaUtil { 29 30 /** 31 * Extends the functionality of {@link Class#getSimpleName()} to include a non-empty string for 32 * anonymous and local classes. 33 * 34 * @param clazz the class for which the simple name is being requested 35 * @param withEnclosingClass specifies if the returned name should be qualified with the name(s) 36 * of the enclosing class/classes of {@code clazz} (if any). This option is ignored 37 * if {@code clazz} denotes an anonymous or local class. 38 * @return the simple name 39 */ 40 public static String getSimpleName(Class<?> clazz, boolean withEnclosingClass) { 41 final String simpleName = safeSimpleName(clazz); 42 if (simpleName.length() != 0) { 43 if (withEnclosingClass) { 44 String prefix = ""; 45 Class<?> enclosingClass = clazz; 46 while ((enclosingClass = enclosingClass.getEnclosingClass()) != null) { 47 prefix = safeSimpleName(enclosingClass) + "." + prefix; 48 } 49 return prefix + simpleName; 50 } 51 return simpleName; 52 } 53 // Must be an anonymous or local class 54 final String name = clazz.getName(); 55 int index = name.indexOf('$'); 56 if (index == -1) { 57 return name; 58 } 59 index = name.lastIndexOf('.', index); 60 if (index == -1) { 61 return name; 62 } 63 return name.substring(index + 1); 64 } 65 66 private static String safeSimpleName(Class<?> clazz) { 67 try { 68 return clazz.getSimpleName(); 69 } catch (InternalError e) { 70 // Scala inner class names do not always start with '$', 71 // causing Class.getSimpleName to throw an InternalError 72 Class<?> enclosingClass = clazz.getEnclosingClass(); 73 String fqn = clazz.getName(); 74 if (enclosingClass == null) { 75 // Should never happen given logic in 76 // Class.getSimpleName but best be safe 77 return fqn; 78 } 79 String enclosingFQN = enclosingClass.getName(); 80 int length = fqn.length(); 81 if (enclosingFQN.length() >= length) { 82 // Should also never happen 83 return fqn; 84 } 85 return fqn.substring(enclosingFQN.length()); 86 } 87 } 88 89 /** 90 * Converts a type name in internal form to an external form. 91 * 92 * @param name the internal name to convert 93 * @param qualified whether the returned name should be qualified with the package name 94 * @param classForNameCompatible specifies if the returned name for array types should be in 95 * {@link Class#forName(String)} format (e.g., {@code "[Ljava.lang.Object;"}, 96 * {@code "[[I"}) or in Java source code format (e.g., {@code "java.lang.Object[]"}, 97 * {@code "int[][]"} ). 98 */ 99 public static String internalNameToJava(String name, boolean qualified, boolean classForNameCompatible) { 100 switch (name.charAt(0)) { 101 case 'L': { 102 String result = name.substring(1, name.length() - 1).replace('/', '.'); 103 if (!qualified) { 104 final int lastDot = result.lastIndexOf('.'); 105 if (lastDot != -1) { 106 result = result.substring(lastDot + 1); 107 } 108 } 109 return result; 110 } 111 case '[': 112 return classForNameCompatible ? name.replace('/', '.') : internalNameToJava(name.substring(1), qualified, classForNameCompatible) + "[]"; 113 default: 114 if (name.length() != 1) { 115 throw new IllegalArgumentException("Illegal internal name: " + name); 116 } 117 return JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0)).getJavaName(); 118 } 119 } 120 121 /** 122 * Convenient shortcut for calling 123 * {@link #appendLocation(StringBuilder, ResolvedJavaMethod, int)} without having to supply a 124 * {@link StringBuilder} instance and convert the result to a string. 125 */ 126 public static String toLocation(ResolvedJavaMethod method, int bci) { 127 return appendLocation(new StringBuilder(), method, bci).toString(); 128 } 129 130 /** 131 * Appends a string representation of a location specified by a given method and bci to a given 132 * {@link StringBuilder}. If a stack trace element with a non-null file name and non-negative 133 * line number is {@linkplain ResolvedJavaMethod#asStackTraceElement(int) available} for the 134 * given method, then the string returned is the {@link StackTraceElement#toString()} value of 135 * the stack trace element, suffixed by the bci location. For example: 136 * 137 * <pre> 138 * java.lang.String.valueOf(String.java:2930) [bci: 12] 139 * </pre> 140 * 141 * Otherwise, the string returned is the value of applying {@link JavaMethod#format(String)} 142 * with the format string {@code "%H.%n(%p)"}, suffixed by the bci location. For example: 143 * 144 * <pre> 145 * java.lang.String.valueOf(int) [bci: 12] 146 * </pre> 147 * 148 * @param sb 149 * @param method 150 * @param bci 151 */ 152 public static StringBuilder appendLocation(StringBuilder sb, ResolvedJavaMethod method, int bci) { 153 if (method != null) { 154 StackTraceElement ste = method.asStackTraceElement(bci); 155 if (ste.getFileName() != null && ste.getLineNumber() > 0) { 156 sb.append(ste); 157 } else { 158 sb.append(method.format("%H.%n(%p)")); 159 } 160 } else { 161 sb.append("Null method"); 162 } 163 return sb.append(" [bci: ").append(bci).append(']'); 164 } 165 166 static void appendProfile(StringBuilder buf, AbstractJavaProfile<?, ?> profile, int bci, String type, String sep) { 167 if (profile != null) { 168 AbstractProfiledItem<?>[] pitems = profile.getItems(); 169 if (pitems != null) { 170 buf.append(String.format("%s@%d:", type, bci)); 171 for (int j = 0; j < pitems.length; j++) { 172 AbstractProfiledItem<?> pitem = pitems[j]; 173 buf.append(String.format(" %.6f (%s)%s", pitem.getProbability(), pitem.getItem(), sep)); 174 } 175 if (profile.getNotRecordedProbability() != 0) { 176 buf.append(String.format(" %.6f <other %s>%s", profile.getNotRecordedProbability(), type, sep)); 177 } else { 178 buf.append(String.format(" <no other %s>%s", type, sep)); 179 } 180 } 181 } 182 } 183 184 /** 185 * Converts a Java source-language class name into the internal form. 186 * 187 * @param className the class name 188 * @return the internal name form of the class name 189 */ 190 public static String toInternalName(String className) { 191 if (className.startsWith("[")) { 192 /* Already in the correct array style. */ 193 return className.replace('.', '/'); 194 } 195 196 StringBuilder result = new StringBuilder(); 197 String base = className; 198 while (base.endsWith("[]")) { 199 result.append("["); 200 base = base.substring(0, base.length() - 2); 201 } 202 203 switch (base) { 204 case "boolean": 205 result.append("Z"); 206 break; 207 case "byte": 208 result.append("B"); 209 break; 210 case "short": 211 result.append("S"); 212 break; 213 case "char": 214 result.append("C"); 215 break; 216 case "int": 217 result.append("I"); 218 break; 219 case "float": 220 result.append("F"); 221 break; 222 case "long": 223 result.append("J"); 224 break; 225 case "double": 226 result.append("D"); 227 break; 228 case "void": 229 result.append("V"); 230 break; 231 default: 232 result.append("L").append(base.replace('.', '/')).append(";"); 233 break; 234 } 235 return result.toString(); 236 } 237 238 /** 239 * Gets a string representation of an object based soley on its class and its 240 * {@linkplain System#identityHashCode(Object) identity hash code}. This avoids and calls to 241 * virtual methods on the object such as {@link Object#hashCode()}. 242 */ 243 public static String identityHashCodeString(Object obj) { 244 if (obj == null) { 245 return "null"; 246 } 247 return obj.getClass().getName() + "@" + System.identityHashCode(obj); 248 } 249 }