1 /* 2 * Copyright (c) 2012, 2018, 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 24 25 package org.graalvm.compiler.printer; 26 27 import java.io.Closeable; 28 import java.io.IOException; 29 import java.lang.reflect.Array; 30 import java.util.Arrays; 31 import java.util.List; 32 import java.util.Map; 33 34 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 35 import org.graalvm.compiler.debug.DebugContext; 36 import org.graalvm.compiler.debug.DebugContext.Scope; 37 import org.graalvm.compiler.graph.Graph; 38 import org.graalvm.compiler.nodes.ConstantNode; 39 import org.graalvm.compiler.nodes.StructuredGraph; 40 import org.graalvm.compiler.nodes.util.JavaConstantFormatter; 41 import org.graalvm.compiler.phases.schedule.SchedulePhase; 42 import org.graalvm.compiler.serviceprovider.GraalServices; 43 44 import jdk.vm.ci.meta.JavaConstant; 45 import jdk.vm.ci.meta.JavaKind; 46 import jdk.vm.ci.meta.JavaType; 47 import jdk.vm.ci.meta.MetaUtil; 48 import jdk.vm.ci.meta.ResolvedJavaMethod; 49 50 interface GraphPrinter extends Closeable, JavaConstantFormatter { 51 52 /** 53 * Starts a new group of graphs with the given name, short name and method byte code index (BCI) 54 * as properties. 55 */ 56 void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException; 57 58 /** 59 * Prints an entire {@link Graph} with the specified title, optionally using short names for 60 * nodes. 61 */ 62 void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException; 63 64 SnippetReflectionProvider getSnippetReflectionProvider(); 65 66 /** 67 * Ends the current group. 68 */ 69 void endGroup() throws IOException; 70 71 @Override 72 void close(); 73 74 /** 75 * Classes whose {@link #toString()} method does not run any untrusted code. 76 */ 77 List<Class<?>> TRUSTED_CLASSES = Arrays.asList( 78 String.class, 79 Class.class, 80 Boolean.class, 81 Byte.class, 82 Character.class, 83 Short.class, 84 Integer.class, 85 Float.class, 86 Long.class, 87 Double.class); 88 int MAX_CONSTANT_TO_STRING_LENGTH = 50; 89 90 /** 91 * Determines if invoking {@link Object#toString()} on an instance of {@code c} will only run 92 * trusted code. 93 */ 94 static boolean isToStringTrusted(Class<?> c) { 95 if (TRUSTED_CLASSES.contains(c)) { 96 return true; 97 } 98 if (GraalServices.isToStringTrusted(c)) { 99 return true; 100 } 101 if (c.getClassLoader() == GraphPrinter.class.getClassLoader()) { 102 return true; 103 } 104 return false; 105 } 106 107 /** 108 * Use the real {@link Object#toString()} method for {@link JavaConstant JavaConstants} that are 109 * wrapping trusted types, other just return the results of {@link JavaConstant#toString()}. 110 */ 111 @Override 112 default String format(JavaConstant constant) { 113 SnippetReflectionProvider snippetReflection = getSnippetReflectionProvider(); 114 if (snippetReflection != null) { 115 if (constant.getJavaKind() == JavaKind.Object) { 116 Object obj = null; 117 /* 118 * Ignore any exceptions on unknown JavaConstant implementations in debugging code. 119 */ 120 try { 121 obj = snippetReflection.asObject(Object.class, constant); 122 } catch (Throwable ex) { 123 } 124 if (obj != null) { 125 return GraphPrinter.constantToString(obj); 126 } 127 } 128 } 129 return constant.toString(); 130 } 131 132 /** 133 * Sets or updates the {@code "rawvalue"} and {@code "toString"} properties in {@code props} for 134 * {@code cn} if it's a boxed Object value and {@code snippetReflection} can access the raw 135 * value. 136 */ 137 default void updateStringPropertiesForConstant(Map<Object, Object> props, ConstantNode cn) { 138 if (cn.isJavaConstant() && cn.getStackKind().isObject()) { 139 String toString = format(cn.asJavaConstant()); 140 String rawvalue = GraphPrinter.truncate(toString); 141 // Overwrite the value inserted by 142 // ConstantNode.getDebugProperties() 143 props.put("rawvalue", rawvalue); 144 if (!rawvalue.equals(toString)) { 145 props.put("toString", toString); 146 } 147 } 148 } 149 150 /** 151 * Replaces all {@link JavaType} elements in {@code args} with the result of 152 * {@link JavaType#getUnqualifiedName()}. 153 * 154 * @return a copy of {@code args} with the above mentioned substitutions or {@code args} if no 155 * substitutions were performed 156 */ 157 default Object[] simplifyClassArgs(Object... args) { 158 Object[] res = args; 159 for (int i = 0; i < args.length; i++) { 160 Object arg = args[i]; 161 if (arg instanceof JavaType) { 162 if (args == res) { 163 res = new Object[args.length]; 164 for (int a = 0; a < i; a++) { 165 res[a] = args[a]; 166 } 167 } 168 res[i] = ((JavaType) arg).getUnqualifiedName(); 169 } else { 170 res[i] = arg; 171 } 172 } 173 return res; 174 } 175 176 static String truncate(String s) { 177 if (s.length() > MAX_CONSTANT_TO_STRING_LENGTH) { 178 return s.substring(0, MAX_CONSTANT_TO_STRING_LENGTH - 3) + "..."; 179 } 180 return s; 181 } 182 183 static String constantToString(Object value) { 184 Class<?> c = value.getClass(); 185 String suffix = ""; 186 if (c.isArray()) { 187 return constantArrayToString(value); 188 } else if (value instanceof Enum) { 189 return ((Enum<?>) value).name(); 190 } else if (isToStringTrusted(c)) { 191 try { 192 return value.toString(); 193 } catch (Throwable t) { 194 suffix = "[toString error: " + t.getClass().getName() + "]"; 195 if (isToStringTrusted(t.getClass())) { 196 try { 197 suffix = "[toString error: " + t + "]"; 198 } catch (Throwable t2) { 199 // No point in going further 200 } 201 } 202 } 203 } 204 return MetaUtil.getSimpleName(c, true) + "@" + Integer.toHexString(System.identityHashCode(value)) + suffix; 205 } 206 207 static String constantArrayToString(Object array) { 208 Class<?> componentType = array.getClass().getComponentType(); 209 assert componentType != null; 210 int arrayLength = Array.getLength(array); 211 StringBuilder buf = new StringBuilder(MetaUtil.getSimpleName(componentType, true)).append('[').append(arrayLength).append("]{"); 212 int length = arrayLength; 213 boolean primitive = componentType.isPrimitive(); 214 for (int i = 0; i < length; i++) { 215 if (primitive) { 216 buf.append(Array.get(array, i)); 217 } else { 218 Object o = ((Object[]) array)[i]; 219 buf.append(o == null ? "null" : constantToString(o)); 220 } 221 if (i != length - 1) { 222 buf.append(", "); 223 } 224 } 225 return buf.append('}').toString(); 226 } 227 228 @SuppressWarnings("try") 229 static StructuredGraph.ScheduleResult getScheduleOrNull(Graph graph) { 230 if (graph instanceof StructuredGraph) { 231 StructuredGraph sgraph = (StructuredGraph) graph; 232 StructuredGraph.ScheduleResult scheduleResult = sgraph.getLastSchedule(); 233 if (scheduleResult == null) { 234 DebugContext debug = graph.getDebug(); 235 try (Scope scope = debug.disable()) { 236 SchedulePhase schedule = new SchedulePhase(graph.getOptions()); 237 schedule.apply(sgraph); 238 scheduleResult = sgraph.getLastSchedule(); 239 } catch (Throwable t) { 240 } 241 } 242 return scheduleResult; 243 } 244 return null; 245 } 246 }