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