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