diff graal/com.oracle.graal.nodeinfo.processor/src/com/oracle/graal/nodeinfo/processor/GraphNodeGenerator.java @ 16895:06c15e88d383

added factory method to all Node classes; replaced Node classes instantiation with calls to factory methods; replaced identity tests on Node classes with ' == <node class>.getGenClass()' idiom
author Doug Simon <doug.simon@oracle.com>
date Mon, 18 Aug 2014 14:04:21 +0200
parents 03a4d382c122
children f90dcdbbb75e
line wrap: on
line diff
--- a/graal/com.oracle.graal.nodeinfo.processor/src/com/oracle/graal/nodeinfo/processor/GraphNodeGenerator.java	Mon Aug 18 13:49:25 2014 +0200
+++ b/graal/com.oracle.graal.nodeinfo.processor/src/com/oracle/graal/nodeinfo/processor/GraphNodeGenerator.java	Mon Aug 18 14:04:21 2014 +0200
@@ -23,6 +23,7 @@
 package com.oracle.graal.nodeinfo.processor;
 
 import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
+import static java.util.Arrays.*;
 import static javax.lang.model.element.Modifier.*;
 
 import java.util.*;
@@ -60,6 +61,11 @@
         this.NodeSuccessorList = getType("com.oracle.graal.graph.NodeSuccessorList");
     }
 
+    /**
+     * Returns a type element given a canonical name.
+     *
+     * @throw {@link NoClassDefFoundError} if a type element does not exist for {@code name}
+     */
     public TypeElement getType(String name) {
         TypeElement typeElement = env.getProcessingEnv().getElementUtils().getTypeElement(name);
         if (typeElement == null) {
@@ -121,14 +127,11 @@
         }
     }
 
-    public boolean isAssignable(Element from, Element to) {
+    public boolean isAssignableWithErasure(Element from, Element to) {
         Types types = env.getProcessingEnv().getTypeUtils();
         TypeMirror fromType = types.erasure(from.asType());
         TypeMirror toType = types.erasure(to.asType());
-        boolean res = types.isAssignable(fromType, toType);
-        // System.out.printf("%s:%s is %sassignable to %s:%s%n", from, fromType, res ? "" : "NOT ",
-// to, toType);
-        return res;
+        return types.isAssignable(fromType, toType);
     }
 
     public void scanFields(TypeElement node, FieldScanner scanner) {
@@ -151,7 +154,7 @@
                         throw new ElementException(field, "Field cannot be both input and successor");
                     } else if (isNonOptionalInput && isOptionalInput) {
                         throw new ElementException(field, "Inputs must be either optional or non-optional");
-                    } else if (isAssignable(field, NodeInputList)) {
+                    } else if (isAssignableWithErasure(field, NodeInputList)) {
                         if (!modifiers.contains(FINAL)) {
                             throw new ElementException(field, "Input list field must be final");
                         }
@@ -162,7 +165,7 @@
                             return;
                         }
                     } else {
-                        if (!isAssignable(field, Node) && field.getKind() == ElementKind.INTERFACE) {
+                        if (!isAssignableWithErasure(field, Node) && field.getKind() == ElementKind.INTERFACE) {
                             throw new ElementException(field, "Input field type must be an interface or assignable to Node");
                         }
                         if (modifiers.contains(FINAL)) {
@@ -179,7 +182,7 @@
                         }
                     }
                 } else if (isSuccessor) {
-                    if (isAssignable(field, NodeSuccessorList)) {
+                    if (isAssignableWithErasure(field, NodeSuccessorList)) {
                         if (!modifiers.contains(FINAL)) {
                             throw new ElementException(field, "Successor list field must be final");
                         }
@@ -190,7 +193,7 @@
                             return;
                         }
                     } else {
-                        if (!isAssignable(field, Node)) {
+                        if (!isAssignableWithErasure(field, Node)) {
                             throw new ElementException(field, "Successor field must be a Node type");
                         }
                         if (modifiers.contains(FINAL)) {
@@ -208,13 +211,13 @@
                     }
 
                 } else {
-                    if (isAssignable(field, Node) && !field.getSimpleName().contentEquals("Null")) {
+                    if (isAssignableWithErasure(field, Node) && !field.getSimpleName().contentEquals("Null")) {
                         throw new ElementException(field, "Suspicious Node field: " + field);
                     }
-                    if (isAssignable(field, NodeInputList)) {
+                    if (isAssignableWithErasure(field, NodeInputList)) {
                         throw new ElementException(field, "Suspicious NodeInputList field");
                     }
-                    if (isAssignable(field, NodeSuccessorList)) {
+                    if (isAssignableWithErasure(field, NodeSuccessorList)) {
                         throw new ElementException(field, "Suspicious NodeSuccessorList field");
                     }
                     if (!scanner.scanDataField(field)) {
@@ -226,6 +229,37 @@
         } while (!isObject(getSuperType(currentClazz).asType()));
     }
 
+    /**
+     * Determines if two parameter lists contain the
+     * {@linkplain Types#isSameType(TypeMirror, TypeMirror) same} types.
+     */
+    private boolean parametersMatch(List<? extends VariableElement> p1, List<? extends VariableElement> p2) {
+        if (p1.size() == p2.size()) {
+            for (int i = 0; i < p1.size(); i++) {
+                if (!env.getProcessingEnv().getTypeUtils().isSameType(p1.get(i).asType(), p2.get(i).asType())) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Searches a type for a method based on a given name and parameter types.
+     */
+    public ExecutableElement findMethod(TypeElement type, String name, List<? extends VariableElement> parameters) {
+        List<? extends ExecutableElement> methods = ElementFilter.methodsIn(type.getEnclosedElements());
+        for (ExecutableElement method : methods) {
+            if (method.getSimpleName().toString().equals(name)) {
+                if (parametersMatch(method.getParameters(), parameters)) {
+                    return method;
+                }
+            }
+        }
+        return null;
+    }
+
     public CodeCompilationUnit process(TypeElement node) {
 
         CodeCompilationUnit compilationUnit = new CodeCompilationUnit();
@@ -239,11 +273,15 @@
         nodeGenElement.setSuperClass(node.asType());
 
         for (ExecutableElement constructor : ElementFilter.constructorsIn(node.getEnclosedElements())) {
-            if (constructor.getModifiers().contains(Modifier.PRIVATE)) {
-                // ignore private constructors
-                continue;
+            if (constructor.getModifiers().contains(PUBLIC)) {
+                throw new ElementException(constructor, "Node class constructor must not be public");
             }
-            nodeGenElement.add(createSuperConstructor(nodeGenElement, constructor));
+
+            checkFactoryMethodExists(node, newClassName, constructor);
+
+            CodeExecutableElement subConstructor = createConstructor(nodeGenElement, constructor);
+            subConstructor.getModifiers().removeAll(Arrays.asList(PUBLIC, PRIVATE, PROTECTED));
+            nodeGenElement.add(subConstructor);
         }
 
         DeclaredType generatedNode = (DeclaredType) ElementUtils.getType(getProcessingEnv(), GeneratedNode.class);
@@ -257,7 +295,33 @@
         return compilationUnit;
     }
 
-    private CodeExecutableElement createSuperConstructor(TypeElement type, ExecutableElement element) {
+    /**
+     * Checks that a public static factory method named {@code "create"} exists in {@code node}
+     * whose signature matches that of a given constructor.
+     *
+     * @throws ElementException if the check fails
+     */
+    private void checkFactoryMethodExists(TypeElement node, String newClassName, ExecutableElement constructor) {
+        ExecutableElement create = findMethod(node, "create", constructor.getParameters());
+        if (create == null) {
+            Formatter f = new Formatter();
+            f.format("public static %s create(", node.getSimpleName());
+            String sep = "";
+            Formatter callArgs = new Formatter();
+            for (VariableElement v : constructor.getParameters()) {
+                f.format("%s%s %s", sep, ElementUtils.getSimpleName(v.asType()), v.getSimpleName());
+                callArgs.format("%s%s", sep, v.getSimpleName());
+                sep = ", ";
+            }
+            f.format(") { return new %s(%s); }", newClassName, callArgs);
+            throw new ElementException(constructor, "Missing Node class factory method '%s'", f);
+        }
+        if (!create.getModifiers().containsAll(asList(PUBLIC, STATIC))) {
+            throw new ElementException(constructor, "Node class factory method must be public and static");
+        }
+    }
+
+    private CodeExecutableElement createConstructor(TypeElement type, ExecutableElement element) {
         CodeExecutableElement executable = CodeExecutableElement.clone(getProcessingEnv(), element);
 
         // to create a constructor we have to set the return type to null.(TODO needs fix)