changeset 7855:6e4fb0ccebb1

Generated factories implement the new NodeFactory interface.
author Christian Humer <christian.humer@gmail.com>
date Mon, 25 Feb 2013 13:13:02 +0100
parents 8e56c6951c86
children a4a8909a6096
files graal/com.oracle.truffle.api.codegen/src/com/oracle/truffle/api/codegen/NodeFactory.java graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeCodeGenerator.java graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeData.java
diffstat 3 files changed, 315 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.codegen/src/com/oracle/truffle/api/codegen/NodeFactory.java	Mon Feb 25 13:13:02 2013 +0100
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012, 2012, 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.
+ *
+ * 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.codegen;
+
+import java.util.*;
+
+/**
+ * Enables the dynamic creation of generated nodes. It provides an convenient way to instantiate
+ * generated node classes without using reflection.
+ */
+public interface NodeFactory<T> {
+
+    /**
+     * Instantiates the node using the arguments array. The arguments length and types must suffice
+     * one of the returned signatures in {@link #getNodeSignatures()}. If the arguments array does
+     * not suffice one of the node signatures an {@link IllegalArgumentException} is thrown.
+     * 
+     * @param arguments the argument values
+     * @return the instantiated node
+     * @throws IllegalArgumentException
+     */
+    T createNode(Object... arguments);
+
+    /**
+     * Instantiates a new specialized variant of the node. This is an optional method and throws an
+     * {@link UnsupportedOperationException} if not supported.
+     * 
+     * @param thisNode the current node
+     * @param specializionClasses
+     * @return the specialized node
+     */
+    T createNodeSpecialized(T thisNode, Class<?>... specializionClasses);
+
+    /**
+     * Returns the node class that will get created by {@link #createNode(Object...)}. The node
+     * class does not match exactly to the instantiated object but they are guaranteed to be
+     * assignable.
+     */
+    Class<T> getNodeClass();
+
+    /**
+     * Returns a list of signatures that can be used to invoke {@link #createNode(Object...)}.
+     */
+    List<List<Class<?>>> getNodeSignatures();
+
+}
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeCodeGenerator.java	Mon Feb 25 13:05:23 2013 +0100
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeCodeGenerator.java	Mon Feb 25 13:13:02 2013 +0100
@@ -31,6 +31,7 @@
 import javax.lang.model.type.*;
 import javax.lang.model.util.*;
 
+import com.oracle.truffle.api.codegen.*;
 import com.oracle.truffle.codegen.processor.*;
 import com.oracle.truffle.codegen.processor.ast.*;
 import com.oracle.truffle.codegen.processor.node.NodeFieldData.ExecutionKind;
@@ -422,10 +423,6 @@
             add(factory, node);
         }
 
-        if (node.getSpecializations() == null) {
-            return;
-        }
-
         if (node.needsFactory() || childTypes.size() > 0) {
             add(new NodeFactoryFactory(context, childTypes), node);
         }
@@ -511,6 +508,15 @@
                 for (SpecializationData specialization : node.getSpecializations()) {
                     add(new SpecializedNodeFactory(context), specialization);
                 }
+
+                TypeMirror nodeFactory = getContext().getEnvironment().getTypeUtils().getDeclaredType(Utils.fromTypeMirror(getContext().getType(NodeFactory.class)), node.getNodeType());
+                clazz.getImplements().add(nodeFactory);
+                clazz.add(createCreateNodeMethod(node, createVisibility));
+                clazz.add(createCreateNodeSpecializedMethod(node, createVisibility));
+                clazz.add(createGetNodeClassMethod(node));
+                clazz.add(createGetNodeSignaturesMethod(node));
+                clazz.add(createGetInstanceMethod(node, clazz.asType(), createVisibility));
+                clazz.add(createInstanceConstant(node, clazz.asType()));
             }
 
             for (NodeData childNode : childTypes.keySet()) {
@@ -531,9 +537,235 @@
                     clazz.add(type);
                 }
             }
+
+            if (node.getParent() == null && node.getDeclaredChildren().size() > 0) {
+                List<NodeData> children = node.getNodeChildren();
+                List<TypeMirror> types = new ArrayList<>();
+                if (node.needsFactory()) {
+                    types.add(node.getNodeType());
+                }
+                for (NodeData child : children) {
+                    types.add(child.getTemplateType().asType());
+                }
+                TypeMirror commonSuperType = Utils.getCommonSuperType(getContext(), types.toArray(new TypeMirror[types.size()]));
+                clazz.add(createGetFactories(node, commonSuperType));
+            }
+
+        }
+
+        private CodeExecutableElement createGetNodeClassMethod(NodeData node) {
+            Types types = getContext().getEnvironment().getTypeUtils();
+            TypeMirror returnType = types.getDeclaredType(Utils.fromTypeMirror(getContext().getType(Class.class)), node.getNodeType());
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getNodeClass");
+            CodeTreeBuilder builder = method.createBuilder();
+            builder.startReturn().typeLiteral(node.getNodeType()).end();
+            return method;
+        }
+
+        private CodeExecutableElement createGetNodeSignaturesMethod(NodeData node) {
+            Types types = getContext().getEnvironment().getTypeUtils();
+            TypeElement listType = Utils.fromTypeMirror(getContext().getType(List.class));
+            TypeMirror classType = getContext().getType(Class.class);
+            TypeMirror returnType = types.getDeclaredType(listType, types.getDeclaredType(listType, classType));
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getNodeSignatures");
+            CodeTreeBuilder builder = method.createBuilder();
+            builder.startReturn();
+            builder.startStaticCall(getContext().getType(Arrays.class), "asList");
+            List<ExecutableElement> constructors = findUserConstructors(node);
+            for (ExecutableElement constructor : constructors) {
+                builder.startGroup();
+                builder.type(getContext().getType(Arrays.class));
+                builder.string(".<").type(getContext().getType(Class.class)).string(">");
+                builder.startCall("asList");
+                for (VariableElement param : constructor.getParameters()) {
+                    builder.typeLiteral(param.asType());
+                }
+                builder.end();
+                builder.end();
+            }
+            builder.end();
+            builder.end();
+            return method;
+        }
+
+        private CodeExecutableElement createCreateNodeMethod(NodeData node, Modifier visibility) {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(), node.getNodeType(), "createNode");
+            CodeVariableElement arguments = new CodeVariableElement(getContext().getType(Object.class), "arguments");
+            method.setVarArgs(true);
+            method.addParameter(arguments);
+
+            if (visibility != null) {
+                method.getModifiers().add(visibility);
+            }
+
+            CodeTreeBuilder builder = method.createBuilder();
+            List<ExecutableElement> signatures = findUserConstructors(node);
+            boolean ifStarted = false;
+
+            for (ExecutableElement element : signatures) {
+                ifStarted = builder.startIf(ifStarted);
+                builder.string("arguments.length == " + element.getParameters().size());
+
+                int index = 0;
+                for (VariableElement param : element.getParameters()) {
+                    builder.string(" && ");
+                    if (!param.asType().getKind().isPrimitive()) {
+                        builder.string("(arguments[" + index + "] == null || ");
+                    }
+                    builder.string("arguments[" + index + "] instanceof ");
+                    builder.type(Utils.boxType(getContext(), param.asType()));
+                    if (!param.asType().getKind().isPrimitive()) {
+                        builder.string(")");
+                    }
+                    index++;
+                }
+                builder.end();
+                builder.startBlock();
+
+                builder.startReturn().startCall("create");
+                index = 0;
+                for (VariableElement param : element.getParameters()) {
+                    builder.startGroup();
+                    builder.string("(").type(param.asType()).string(") ");
+                    builder.string("arguments[").string(String.valueOf(index)).string("]");
+                    builder.end();
+                    index++;
+                }
+                builder.end().end();
+
+                builder.end(); // block
+            }
+
+            builder.startElseBlock();
+            builder.startThrow().startNew(getContext().getType(IllegalArgumentException.class));
+            builder.doubleQuote("Invalid create signature.");
+            builder.end().end();
+            builder.end(); // else block
+            return method;
+        }
+
+        private CodeExecutableElement createCreateNodeSpecializedMethod(NodeData node, Modifier visibility) {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(), node.getNodeType(), "createNodeSpecialized");
+            CodeVariableElement nodeParam = new CodeVariableElement(node.getNodeType(), "thisNode");
+            CodeVariableElement arguments = new CodeVariableElement(getContext().getType(Class.class), "types");
+            method.addParameter(nodeParam);
+            method.addParameter(arguments);
+            method.setVarArgs(true);
+            if (visibility != null) {
+                method.getModifiers().add(visibility);
+            }
+
+            CodeTreeBuilder builder = method.createBuilder();
+            if (!node.needsRewrites(getContext())) {
+                builder.startThrow().startNew(getContext().getType(UnsupportedOperationException.class)).end().end();
+            } else {
+                builder.startIf();
+                builder.string("types.length == 1");
+                builder.end();
+                builder.startBlock();
+
+                builder.startReturn().startCall("createSpecialized");
+                builder.string("thisNode");
+                builder.string("types[0]");
+                builder.end().end();
+
+                builder.end();
+                builder.startElseBlock();
+                builder.startThrow().startNew(getContext().getType(IllegalArgumentException.class));
+                builder.doubleQuote("Invalid createSpecialized signature.");
+                builder.end().end();
+                builder.end();
+            }
+
+            return method;
+        }
+
+        private ExecutableElement createGetInstanceMethod(NodeData node, TypeMirror factoryType, Modifier visibility) {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(), factoryType, "getInstance");
+            if (visibility != null) {
+                method.getModifiers().add(visibility);
+            }
+            method.getModifiers().add(Modifier.STATIC);
+
+            String varName = instanceVarName(node);
+
+            CodeTreeBuilder builder = method.createBuilder();
+            builder.startIf();
+            builder.string(varName).string(" == null");
+            builder.end().startBlock();
+
+            builder.startStatement();
+            builder.string(varName);
+            builder.string(" = ");
+            builder.startNew(factoryClassName(node)).end();
+            builder.end();
+
+            builder.end();
+            builder.startReturn().string(varName).end();
+            return method;
+        }
+
+        private String instanceVarName(NodeData node) {
+            if (node.getParent() != null) {
+                return Utils.firstLetterLowerCase(factoryClassName(node)) + "Instance";
+            } else {
+                return "instance";
+            }
+        }
+
+        private CodeVariableElement createInstanceConstant(NodeData node, TypeMirror factoryType) {
+            String varName = instanceVarName(node);
+            CodeVariableElement var = new CodeVariableElement(modifiers(), factoryType, varName);
+            var.getModifiers().add(Modifier.PRIVATE);
+            var.getModifiers().add(Modifier.STATIC);
+            return var;
+        }
+
+        private ExecutableElement createGetFactories(NodeData node, TypeMirror commonSuperType) {
+            Types types = getContext().getEnvironment().getTypeUtils();
+            TypeMirror classType = getContext().getType(NodeFactory.class);
+            TypeMirror classWithWildcards = types.getDeclaredType(Utils.fromTypeMirror(classType), types.getWildcardType(commonSuperType, null));
+            TypeMirror listType = types.getDeclaredType(Utils.fromTypeMirror(getContext().getType(List.class)), classWithWildcards);
+
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), listType, "getFactories");
+
+            CodeTreeBuilder builder = method.createBuilder();
+            builder.startReturn();
+            builder.startStaticCall(getContext().getType(Arrays.class), "asList");
+            List<NodeData> children = node.getNodeChildren();
+            if (node.needsFactory()) {
+                children.add(node);
+            }
+
+            for (NodeData child : children) {
+                builder.startGroup();
+                NodeData childNode = child;
+                List<NodeData> factories = new ArrayList<>();
+                while (childNode.getParent() != null) {
+                    factories.add(childNode);
+                    childNode = childNode.getParent();
+                }
+                Collections.reverse(factories);
+                for (NodeData nodeData : factories) {
+                    builder.string(factoryClassName(nodeData)).string(".");
+                }
+                builder.string("getInstance()");
+                builder.end();
+            }
+            builder.end();
+            builder.end();
+            return method;
         }
 
         private void createFactoryMethods(NodeData node, CodeTypeElement clazz, Modifier createVisibility) {
+            List<ExecutableElement> constructors = findUserConstructors(node);
+            for (ExecutableElement constructor : constructors) {
+                clazz.add(createCreateMethod(node, createVisibility, constructor));
+            }
+        }
+
+        private List<ExecutableElement> findUserConstructors(NodeData node) {
+            List<ExecutableElement> constructors = new ArrayList<>();
             for (ExecutableElement constructor : ElementFilter.constructorsIn(Utils.fromTypeMirror(node.getNodeType()).getEnclosedElements())) {
                 if (constructor.getModifiers().contains(PRIVATE)) {
                     continue;
@@ -543,9 +775,9 @@
                 if (constructor.getParameters().size() == 1 && typeEquals(constructor.getParameters().get(0).asType(), node.getNodeType())) {
                     continue;
                 }
-
-                clazz.add(createCreateMethod(node, createVisibility, constructor));
+                constructors.add(constructor);
             }
+            return constructors;
         }
 
         private CodeExecutableElement createCreateMethod(NodeData node, Modifier visibility, ExecutableElement constructor) {
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeData.java	Mon Feb 25 13:05:23 2013 +0100
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeData.java	Mon Feb 25 13:13:02 2013 +0100
@@ -71,6 +71,17 @@
         return !noSpecialization;
     }
 
+    public List<NodeData> getNodeChildren() {
+        List<NodeData> children = new ArrayList<>();
+        for (NodeData child : getDeclaredChildren()) {
+            if (child.needsFactory()) {
+                children.add(child);
+            }
+            children.addAll(child.getNodeChildren());
+        }
+        return children;
+    }
+
     void setDeclaredChildren(List<NodeData> declaredChildren) {
         this.declaredChildren = declaredChildren;