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 static org.graalvm.compiler.core.common.util.Util.JAVA_SPECIFICATION_VERSION; 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.HashSet; 32 import java.util.Map; 33 import java.util.Set; 34 35 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 36 import org.graalvm.compiler.core.common.util.ModuleAPI; 37 import org.graalvm.compiler.graph.Graph; 38 import org.graalvm.compiler.nodes.ConstantNode; 39 40 import jdk.vm.ci.meta.JavaConstant; 41 import jdk.vm.ci.meta.JavaKind; 42 import jdk.vm.ci.meta.MetaUtil; 43 import jdk.vm.ci.meta.ResolvedJavaMethod; 44 import jdk.vm.ci.runtime.JVMCI; 45 import jdk.vm.ci.services.Services; 46 47 interface GraphPrinter extends Closeable { 48 49 /** 50 * Starts a new group of graphs with the given name, short name and method byte code index (BCI) 51 * as properties. 52 */ 53 void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException; 54 55 /** 56 * Prints an entire {@link Graph} with the specified title, optionally using short names for 57 * nodes. 58 */ 59 void print(Graph graph, String title, Map<Object, Object> properties) throws IOException; 60 61 SnippetReflectionProvider getSnippetReflectionProvider(); 62 63 /** 64 * Ends the current group. 65 */ 66 void endGroup() throws IOException; 67 68 @Override 69 void close(); 70 71 /** 72 * A JVMCI package {@linkplain Services#exportJVMCITo(Class) dynamically exported} to trusted 73 * modules. 74 */ 75 String JVMCI_RUNTIME_PACKAGE = JVMCI.class.getPackage().getName(); 76 77 /** 78 * {@code jdk.vm.ci} module. 79 */ 80 Object JVMCI_MODULE = JAVA_SPECIFICATION_VERSION < 9 ? null : ModuleAPI.getModule.invoke(Services.class); 81 82 /** 83 * Classes whose {@link #toString()} method does not run any untrusted code. 84 */ 85 Set<Class<?>> TRUSTED_CLASSES = new HashSet<>(Arrays.asList( 86 String.class, 87 Class.class, 88 Boolean.class, 89 Byte.class, 90 Character.class, 91 Short.class, 92 Integer.class, 93 Float.class, 94 Long.class, 95 Double.class)); 96 int MAX_CONSTANT_TO_STRING_LENGTH = 50; 97 98 /** 99 * Determines if invoking {@link Object#toString()} on an instance of {@code c} will only run 100 * trusted code. 101 */ 102 static boolean isToStringTrusted(Class<?> c) { 103 if (TRUSTED_CLASSES.contains(c)) { 104 return true; 105 } 106 if (JAVA_SPECIFICATION_VERSION < 9) { 107 if (c.getClassLoader() == Services.class.getClassLoader()) { 108 // Loaded by the JVMCI class loader 109 return true; 110 } 111 } else { 112 Object module = ModuleAPI.getModule.invoke(c); 113 if (JVMCI_MODULE == module || (Boolean) ModuleAPI.isExportedTo.invoke(JVMCI_MODULE, JVMCI_RUNTIME_PACKAGE, module)) { 114 // Can access non-statically-exported package in JVMCI 115 return true; 116 } 117 } 118 return false; 119 } 120 121 /** 122 * Sets or updates the {@code "rawvalue"} and {@code "toString"} properties in {@code props} for 123 * {@code cn} if it's a boxed Object value and {@code snippetReflection} can access the raw 124 * value. 125 */ 126 default void updateStringPropertiesForConstant(Map<Object, Object> props, ConstantNode cn) { 127 SnippetReflectionProvider snippetReflection = getSnippetReflectionProvider(); 128 if (snippetReflection != null && cn.getValue() instanceof JavaConstant) { 129 JavaConstant constant = (JavaConstant) cn.getValue(); 130 if (constant.getJavaKind() == JavaKind.Object) { 131 Object obj = snippetReflection.asObject(Object.class, constant); 132 if (obj != null) { 133 String toString = GraphPrinter.constantToString(obj); 134 String rawvalue = GraphPrinter.truncate(toString); 135 // Overwrite the value inserted by 136 // ConstantNode.getDebugProperties() 137 props.put("rawvalue", rawvalue); 138 if (!rawvalue.equals(toString)) { 139 props.put("toString", toString); 140 } 141 } 142 } 143 } 144 } 145 146 static String truncate(String s) { 147 if (s.length() > MAX_CONSTANT_TO_STRING_LENGTH) { 148 return s.substring(0, MAX_CONSTANT_TO_STRING_LENGTH - 3) + "..."; 149 } 150 return s; 151 } 152 153 static String constantToString(Object value) { 154 Class<?> c = value.getClass(); 155 if (c.isArray()) { 156 return constantArrayToString(value); 157 } else if (value instanceof Enum) { 158 return ((Enum<?>) value).name(); 159 } else if (isToStringTrusted(c)) { 160 return value.toString(); 161 } 162 return MetaUtil.getSimpleName(c, true) + "@" + System.identityHashCode(value); 163 164 } 165 166 static String constantArrayToString(Object array) { 167 Class<?> componentType = array.getClass().getComponentType(); 168 assert componentType != null; 169 int arrayLength = Array.getLength(array); 170 StringBuilder buf = new StringBuilder(MetaUtil.getSimpleName(componentType, true)).append('[').append(arrayLength).append("]{"); 171 int length = arrayLength; 172 boolean primitive = componentType.isPrimitive(); 173 for (int i = 0; i < length; i++) { 174 if (primitive) { 175 buf.append(Array.get(array, i)); 176 } else { 177 Object o = ((Object[]) array)[i]; 178 buf.append(o == null ? "null" : constantToString(o)); 179 } 180 if (i != length - 1) { 181 buf.append(", "); 182 } 183 } 184 return buf.append('}').toString(); 185 } 186 }