Mercurial > hg > truffle
diff truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java @ 21951:9c8c0937da41
Moving all sources into truffle subdirectory
author | Jaroslav Tulach <jaroslav.tulach@oracle.com> |
---|---|
date | Wed, 17 Jun 2015 10:58:08 +0200 |
parents | graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java@f4cd6b1c2efc |
children | 5023b913e2ba |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Wed Jun 17 10:58:08 2015 +0200 @@ -0,0 +1,841 @@ +/* + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.nodes; + +import java.io.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; + +import sun.misc.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind; +import com.oracle.truffle.api.source.*; + +/** + * Utility class that manages the special access methods for node instances. + */ +public final class NodeUtil { + + /** + * Interface that allows the customization of field offsets used for {@link Unsafe} field + * accesses. + */ + public interface FieldOffsetProvider { + + long objectFieldOffset(Field field); + + int getTypeSize(Class<?> clazz); + } + + static Iterator<Node> makeIterator(Node node) { + return node.getNodeClass().makeIterator(node); + } + + public static Iterator<Node> makeRecursiveIterator(Node node) { + return new RecursiveNodeIterator(node); + } + + private static final class RecursiveNodeIterator implements Iterator<Node> { + private final List<Iterator<Node>> iteratorStack = new ArrayList<>(); + + public RecursiveNodeIterator(final Node node) { + iteratorStack.add(new Iterator<Node>() { + + private boolean visited; + + public void remove() { + throw new UnsupportedOperationException(); + } + + public Node next() { + if (visited) { + throw new NoSuchElementException(); + } + visited = true; + return node; + } + + public boolean hasNext() { + return !visited; + } + }); + } + + public boolean hasNext() { + return peekIterator() != null; + } + + public Node next() { + Iterator<Node> iterator = peekIterator(); + if (iterator == null) { + throw new NoSuchElementException(); + } + + Node node = iterator.next(); + if (node != null) { + Iterator<Node> childIterator = makeIterator(node); + if (childIterator.hasNext()) { + iteratorStack.add(childIterator); + } + } + return node; + } + + private Iterator<Node> peekIterator() { + int tos = iteratorStack.size() - 1; + while (tos >= 0) { + Iterator<Node> iterable = iteratorStack.get(tos); + if (iterable.hasNext()) { + return iterable; + } else { + iteratorStack.remove(tos--); + } + } + return null; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + + @SuppressWarnings("unchecked") + public static <T extends Node> T cloneNode(T orig) { + return (T) orig.deepCopy(); + } + + static Node deepCopyImpl(Node orig) { + final Node clone = orig.copy(); + NodeClass nodeClass = clone.getNodeClass(); + + nodeClass.getParentField().putObject(clone, null); + + for (NodeFieldAccessor childField : nodeClass.getChildFields()) { + Node child = (Node) childField.getObject(orig); + if (child != null) { + Node clonedChild = child.deepCopy(); + nodeClass.getParentField().putObject(clonedChild, clone); + childField.putObject(clone, clonedChild); + } + } + for (NodeFieldAccessor childrenField : nodeClass.getChildrenFields()) { + Object[] children = (Object[]) childrenField.getObject(orig); + if (children != null) { + Object[] clonedChildren = (Object[]) Array.newInstance(children.getClass().getComponentType(), children.length); + for (int i = 0; i < children.length; i++) { + if (children[i] != null) { + Node clonedChild = ((Node) children[i]).deepCopy(); + clonedChildren[i] = clonedChild; + nodeClass.getParentField().putObject(clonedChild, clone); + } + } + childrenField.putObject(clone, clonedChildren); + } + } + for (NodeFieldAccessor cloneableField : nodeClass.getCloneableFields()) { + Object cloneable = cloneableField.getObject(clone); + if (cloneable != null && cloneable == cloneableField.getObject(orig)) { + cloneableField.putObject(clone, ((NodeCloneable) cloneable).clone()); + } + } + return clone; + } + + public static List<Node> findNodeChildren(Node node) { + List<Node> nodes = new ArrayList<>(); + NodeClass nodeClass = node.getNodeClass(); + + for (NodeFieldAccessor nodeField : nodeClass.getChildFields()) { + Object child = nodeField.getObject(node); + if (child != null) { + nodes.add((Node) child); + } + } + for (NodeFieldAccessor nodeField : nodeClass.getChildrenFields()) { + Object[] children = (Object[]) nodeField.getObject(node); + if (children != null) { + for (Object child : children) { + if (child != null) { + nodes.add((Node) child); + } + } + } + } + + return nodes; + } + + public static <T extends Node> T nonAtomicReplace(Node oldNode, T newNode, CharSequence reason) { + oldNode.replaceHelper(newNode, reason); + return newNode; + } + + public static boolean replaceChild(Node parent, Node oldChild, Node newChild) { + NodeClass nodeClass = parent.getNodeClass(); + + for (NodeFieldAccessor nodeField : nodeClass.getChildFields()) { + if (nodeField.getObject(parent) == oldChild) { + assert assertAssignable(nodeField, newChild); + nodeField.putObject(parent, newChild); + return true; + } + } + + for (NodeFieldAccessor nodeField : nodeClass.getChildrenFields()) { + Object arrayObject = nodeField.getObject(parent); + if (arrayObject != null) { + Object[] array = (Object[]) arrayObject; + for (int i = 0; i < array.length; i++) { + if (array[i] == oldChild) { + assert assertAssignable(nodeField, newChild); + array[i] = newChild; + return true; + } + } + } + } + return false; + } + + private static boolean assertAssignable(NodeFieldAccessor field, Object newValue) { + if (newValue == null) { + return true; + } + if (field.getKind() == NodeFieldKind.CHILD) { + if (field.getType().isAssignableFrom(newValue.getClass())) { + return true; + } else { + assert false : "Child class " + newValue.getClass().getName() + " is not assignable to field \"" + field.getName() + "\" of type " + field.getType().getName(); + return false; + } + } else if (field.getKind() == NodeFieldKind.CHILDREN) { + if (field.getType().getComponentType().isAssignableFrom(newValue.getClass())) { + return true; + } else { + assert false : "Child class " + newValue.getClass().getName() + " is not assignable to field \"" + field.getName() + "\" of type " + field.getType().getName(); + return false; + } + } + throw new IllegalArgumentException(); + } + + /** + * Finds the field in a parent node, if any, that holds a specified child node. + * + * @return the field (possibly an array) holding the child, {@code null} if not found. + */ + public static NodeFieldAccessor findChildField(Node parent, Node child) { + assert child != null; + NodeClass parentNodeClass = parent.getNodeClass(); + + for (NodeFieldAccessor field : parentNodeClass.getChildFields()) { + if (field.getObject(parent) == child) { + return field; + } + } + + for (NodeFieldAccessor field : parentNodeClass.getChildrenFields()) { + Object arrayObject = field.getObject(parent); + if (arrayObject != null) { + Object[] array = (Object[]) arrayObject; + for (int i = 0; i < array.length; i++) { + if (array[i] == child) { + return field; + } + } + } + } + return null; + } + + /** + * Determines whether a proposed child replacement would be safe: structurally and type. + */ + public static boolean isReplacementSafe(Node parent, Node oldChild, Node newChild) { + assert newChild != null; + if (parent != null) { + final NodeFieldAccessor field = findChildField(parent, oldChild); + if (field != null) { + switch (field.getKind()) { + case CHILD: + return field.getType().isAssignableFrom(newChild.getClass()); + case CHILDREN: + return field.getType().getComponentType().isAssignableFrom(newChild.getClass()); + default: + throw new IllegalStateException(); + } + } + } + return false; + } + + /** + * Executes a closure for every non-null child of the parent node. + * + * @return {@code true} if all children were visited, {@code false} otherwise + */ + public static boolean forEachChild(Node parent, NodeVisitor visitor) { + Objects.requireNonNull(visitor); + NodeClass parentNodeClass = parent.getNodeClass(); + + for (NodeFieldAccessor field : parentNodeClass.getChildFields()) { + Object child = field.getObject(parent); + if (child != null) { + if (!visitor.visit((Node) child)) { + return false; + } + } + } + + for (NodeFieldAccessor field : parentNodeClass.getChildrenFields()) { + Object arrayObject = field.getObject(parent); + if (arrayObject != null) { + Object[] array = (Object[]) arrayObject; + for (int i = 0; i < array.length; i++) { + Object child = array[i]; + if (child != null) { + if (!visitor.visit((Node) child)) { + return false; + } + } + } + } + } + + return true; + } + + static boolean forEachChildRecursive(Node parent, NodeVisitor visitor) { + NodeClass parentNodeClass = parent.getNodeClass(); + + for (NodeFieldAccessor field : parentNodeClass.getChildFields()) { + if (!visitChild((Node) field.getObject(parent), visitor)) { + return false; + } + } + + for (NodeFieldAccessor field : parentNodeClass.getChildrenFields()) { + Object arrayObject = field.getObject(parent); + if (arrayObject == null) { + continue; + } + Object[] array = (Object[]) arrayObject; + for (int i = 0; i < array.length; i++) { + if (!visitChild((Node) array[i], visitor)) { + return false; + } + } + } + + return true; + } + + private static boolean visitChild(Node child, NodeVisitor visitor) { + if (child == null) { + return true; + } + if (!visitor.visit(child)) { + return false; + } + if (!forEachChildRecursive(child, visitor)) { + return false; + } + return true; + } + + /** Returns all declared fields in the class hierarchy. */ + static Field[] getAllFields(Class<? extends Object> clazz) { + Field[] declaredFields = clazz.getDeclaredFields(); + if (clazz.getSuperclass() != null) { + return concat(getAllFields(clazz.getSuperclass()), declaredFields); + } + return declaredFields; + } + + public static <T> T[] concat(T[] first, T[] second) { + T[] result = Arrays.copyOf(first, first.length + second.length); + System.arraycopy(second, 0, result, first.length, second.length); + return result; + } + + /** + * Get the nth parent of a node, where the 0th parent is the node itself. Returns null if there + * are less than n ancestors. + */ + public static Node getNthParent(Node node, int n) { + Node parent = node; + + for (int i = 0; i < n; i++) { + parent = parent.getParent(); + + if (parent == null) { + return null; + } + } + + return parent; + } + + /** find annotation in class/interface hierarchy. */ + public static <T extends Annotation> T findAnnotation(Class<?> clazz, Class<T> annotationClass) { + if (clazz.getAnnotation(annotationClass) != null) { + return clazz.getAnnotation(annotationClass); + } else { + for (Class<?> intf : clazz.getInterfaces()) { + if (intf.getAnnotation(annotationClass) != null) { + return intf.getAnnotation(annotationClass); + } + } + if (clazz.getSuperclass() != null) { + return findAnnotation(clazz.getSuperclass(), annotationClass); + } + } + return null; + } + + public static <T> T findParent(Node start, Class<T> clazz) { + Node parent = start.getParent(); + if (parent == null) { + return null; + } else if (clazz.isInstance(parent)) { + return clazz.cast(parent); + } else { + return findParent(parent, clazz); + } + } + + public static <T> List<T> findAllParents(Node start, Class<T> clazz) { + List<T> parents = new ArrayList<>(); + T parent = findParent(start, clazz); + while (parent != null) { + parents.add(parent); + parent = findParent((Node) parent, clazz); + } + return parents; + } + + public static List<Node> collectNodes(Node parent, Node child) { + List<Node> nodes = new ArrayList<>(); + Node current = child; + while (current != null) { + nodes.add(current); + if (current == parent) { + return nodes; + } + current = current.getParent(); + } + throw new IllegalArgumentException("Node " + parent + " is not a parent of " + child + "."); + } + + public static <T> T findFirstNodeInstance(Node root, Class<T> clazz) { + if (clazz.isInstance(root)) { + return clazz.cast(root); + } + for (Node child : root.getChildren()) { + T node = findFirstNodeInstance(child, clazz); + if (node != null) { + return node; + } + } + return null; + } + + public static <T> List<T> findAllNodeInstances(final Node root, final Class<T> clazz) { + final List<T> nodeList = new ArrayList<>(); + root.accept(new NodeVisitor() { + public boolean visit(Node node) { + if (clazz.isInstance(node)) { + nodeList.add(clazz.cast(node)); + } + return true; + } + }); + return nodeList; + } + + public static int countNodes(Node root) { + return countNodes(root, NodeCountFilter.NO_FILTER); + } + + public static int countNodes(Node root, NodeCountFilter filter) { + NodeCounter counter = new NodeCounter(filter); + root.accept(counter); + return counter.count; + } + + public interface NodeCountFilter { + + NodeCountFilter NO_FILTER = new NodeCountFilter() { + + public boolean isCounted(Node node) { + return true; + } + }; + + boolean isCounted(Node node); + + } + + public static String printCompactTreeToString(Node node) { + StringWriter out = new StringWriter(); + printCompactTree(new PrintWriter(out), null, node, 1); + return out.toString(); + } + + public static void printCompactTree(OutputStream out, Node node) { + printCompactTree(new PrintWriter(out), null, node, 1); + } + + private static void printCompactTree(PrintWriter p, Node parent, Node node, int level) { + if (node == null) { + return; + } + for (int i = 0; i < level; i++) { + p.print(" "); + } + if (parent == null) { + p.println(nodeName(node)); + } else { + p.print(getNodeFieldName(parent, node, "unknownField")); + p.print(" = "); + p.println(nodeName(node)); + } + + for (Node child : node.getChildren()) { + printCompactTree(p, node, child, level + 1); + } + p.flush(); + } + + public static String printSourceAttributionTree(Node node) { + StringWriter out = new StringWriter(); + printSourceAttributionTree(new PrintWriter(out), null, node, 1); + return out.toString(); + } + + public static void printSourceAttributionTree(OutputStream out, Node node) { + printSourceAttributionTree(new PrintWriter(out), null, node, 1); + } + + public static void printSourceAttributionTree(PrintWriter out, Node node) { + printSourceAttributionTree(out, null, node, 1); + } + + private static void printSourceAttributionTree(PrintWriter p, Node parent, Node node, int level) { + if (node == null) { + return; + } + if (parent == null) { + // Add some preliminary information before starting with the root node + final SourceSection sourceSection = node.getSourceSection(); + if (sourceSection != null) { + final String txt = sourceSection.getSource().getCode(); + p.println("Full source len=(" + txt.length() + ") ___" + txt + "___"); + p.println("AST source attribution:"); + } + } + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < level; i++) { + sb.append("| "); + } + + if (parent != null) { + sb.append(getNodeFieldName(parent, node, "")); + } + + sb.append(" (" + node.getClass().getSimpleName() + ") "); + + sb.append(printSyntaxTags(node)); + + sb.append(displaySourceAttribution(node)); + p.println(sb.toString()); + + for (Node child : node.getChildren()) { + printSourceAttributionTree(p, node, child, level + 1); + } + p.flush(); + } + + private static String getNodeFieldName(Node parent, Node node, String defaultName) { + NodeFieldAccessor[] fields = parent.getNodeClass().getFields(); + for (NodeFieldAccessor field : fields) { + Object value = field.loadValue(parent); + if (field.getKind() == NodeFieldKind.CHILD && value == node) { + return field.getName(); + } else if (field.getKind() == NodeFieldKind.CHILDREN) { + int index = 0; + for (Object arrayNode : (Object[]) value) { + if (arrayNode == node) { + return field.getName() + "[" + index + "]"; + } + index++; + } + } + } + return defaultName; + } + + /** + * Returns a string listing the {@linkplain SyntaxTag syntax tags}, if any, associated with a + * node: + * <ul> + * <li>"[{@linkplain StandardSyntaxTag#STATEMENT STATEMENT}, + * {@linkplain StandardSyntaxTag#ASSIGNMENT ASSIGNMENT}]" if tags have been applied;</li> + * <li>"[]" if the node supports tags, but none are present; and</li> + * <li>"" if the node does not support tags.</li> + * </ul> + */ + public static String printSyntaxTags(final Object node) { + if (node instanceof WrapperNode) { + final Probe probe = ((WrapperNode) node).getProbe(); + final Collection<SyntaxTag> syntaxTags = probe.getSyntaxTags(); + final StringBuilder sb = new StringBuilder(); + String prefix = ""; + sb.append("["); + for (SyntaxTag tag : syntaxTags) { + sb.append(prefix); + prefix = ","; + sb.append(tag.toString()); + } + sb.append("]"); + return sb.toString(); + } + return ""; + } + + /** + * Prints a human readable form of a {@link Node} AST to the given {@link PrintStream}. This + * print method does not check for cycles in the node structure. + * + * @param out the stream to print to. + * @param node the root node to write + */ + public static void printTree(OutputStream out, Node node) { + printTree(new PrintWriter(out), node); + } + + public static String printTreeToString(Node node) { + StringWriter out = new StringWriter(); + printTree(new PrintWriter(out), node); + return out.toString(); + } + + public static void printTree(PrintWriter p, Node node) { + printTree(p, node, 1); + p.println(); + p.flush(); + } + + private static void printTree(PrintWriter p, Node node, int level) { + if (node == null) { + p.print("null"); + return; + } + + p.print(nodeName(node)); + + ArrayList<NodeFieldAccessor> childFields = new ArrayList<>(); + String sep = ""; + p.print("("); + for (NodeFieldAccessor field : NodeClass.get(node).getFields()) { + if (field.getKind() == NodeFieldKind.CHILD || field.getKind() == NodeFieldKind.CHILDREN) { + childFields.add(field); + } else if (field.getKind() == NodeFieldKind.DATA) { + p.print(sep); + sep = ", "; + + p.print(field.getName()); + p.print(" = "); + p.print(field.loadValue(node)); + } + } + p.print(")"); + + if (childFields.size() != 0) { + p.print(" {"); + for (NodeFieldAccessor field : childFields) { + printNewLine(p, level); + p.print(field.getName()); + + Object value = field.loadValue(node); + if (value == null) { + p.print(" = null "); + } else if (field.getKind() == NodeFieldKind.CHILD) { + p.print(" = "); + printTree(p, (Node) value, level + 1); + } else if (field.getKind() == NodeFieldKind.CHILDREN) { + printChildren(p, level, value); + } + } + printNewLine(p, level - 1); + p.print("}"); + } + } + + private static void printChildren(PrintWriter p, int level, Object value) { + String sep; + Object[] children = (Object[]) value; + p.print(" = ["); + sep = ""; + for (Object child : children) { + p.print(sep); + sep = ", "; + printTree(p, (Node) child, level + 1); + } + p.print("]"); + } + + private static void printNewLine(PrintWriter p, int level) { + p.println(); + for (int i = 0; i < level; i++) { + p.print(" "); + } + } + + private static String nodeName(Node node) { + return node.getClass().getSimpleName(); + } + + private static String displaySourceAttribution(Node node) { + final SourceSection section = node.getSourceSection(); + if (section instanceof NullSourceSection) { + return "source: " + section.getShortDescription(); + } + if (section != null) { + final String srcText = section.getCode(); + final StringBuilder sb = new StringBuilder(); + sb.append("source:"); + sb.append(" (" + section.getCharIndex() + "," + (section.getCharEndIndex() - 1) + ")"); + sb.append(" line=" + section.getLineLocation().getLineNumber()); + sb.append(" len=" + srcText.length()); + sb.append(" text=\"" + srcText + "\""); + return sb.toString(); + } + return ""; + } + + public static boolean verify(Node root) { + Iterable<Node> children = root.getChildren(); + for (Node child : children) { + if (child != null) { + if (child.getParent() != root) { + throw new AssertionError(toStringWithClass(child) + ": actual parent=" + toStringWithClass(child.getParent()) + " expected parent=" + toStringWithClass(root)); + } + verify(child); + } + } + return true; + } + + private static String toStringWithClass(Object obj) { + return obj == null ? "null" : obj + "(" + obj.getClass().getName() + ")"; + } + + static void traceRewrite(Node oldNode, Node newNode, CharSequence reason) { + if (TruffleOptions.TraceRewritesFilterFromCost != null) { + if (filterByKind(oldNode, TruffleOptions.TraceRewritesFilterFromCost)) { + return; + } + } + + if (TruffleOptions.TraceRewritesFilterToCost != null) { + if (filterByKind(newNode, TruffleOptions.TraceRewritesFilterToCost)) { + return; + } + } + + String filter = TruffleOptions.TraceRewritesFilterClass; + Class<? extends Node> from = oldNode.getClass(); + Class<? extends Node> to = newNode.getClass(); + if (filter != null && (filterByContainsClassName(from, filter) || filterByContainsClassName(to, filter))) { + return; + } + + final SourceSection reportedSourceSection = oldNode.getEncapsulatingSourceSection(); + + PrintStream out = System.out; + out.printf("[truffle] rewrite %-50s |From %-40s |To %-40s |Reason %s%s%n", oldNode.toString(), formatNodeInfo(oldNode), formatNodeInfo(newNode), + reason != null && reason.length() > 0 ? reason : "unknown", reportedSourceSection != null ? " at " + reportedSourceSection.getShortDescription() : ""); + } + + private static String formatNodeInfo(Node node) { + String cost = "?"; + switch (node.getCost()) { + case NONE: + cost = "G"; + break; + case MONOMORPHIC: + cost = "M"; + break; + case POLYMORPHIC: + cost = "P"; + break; + case MEGAMORPHIC: + cost = "G"; + break; + default: + cost = "?"; + break; + } + return cost + " " + node.getClass().getSimpleName(); + } + + private static boolean filterByKind(Node node, NodeCost cost) { + return node.getCost() == cost; + } + + private static boolean filterByContainsClassName(Class<? extends Node> from, String filter) { + Class<?> currentFrom = from; + while (currentFrom != null) { + if (currentFrom.getName().contains(filter)) { + return false; + } + currentFrom = currentFrom.getSuperclass(); + } + return true; + } + + private static final class NodeCounter implements NodeVisitor { + + public int count; + private final NodeCountFilter filter; + + public NodeCounter(NodeCountFilter filter) { + this.filter = filter; + } + + public boolean visit(Node node) { + if (filter.isCounted(node)) { + count++; + } + return true; + } + + } +}