changeset 18775:a069a87b9a02

Truffle-DSL: Added tests and verification of overridable generic execute methods.
author Christian Humer <christian.humer@gmail.com>
date Mon, 05 Jan 2015 01:31:08 +0100
parents 674c8a6d5e6c
children c0fb70634640
files graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteMethodTest.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeBaseFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeChildData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/ExecutableTypeMethodParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java
diffstat 6 files changed, 220 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteMethodTest.java	Mon Jan 05 01:31:08 2015 +0100
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2014, 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.dsl.test;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.nodes.*;
+
+public class ExecuteMethodTest {
+
+    private static final String NO_EXECUTE = "No accessible and overridable generic execute method found. Generic execute methods usually have the signature 'public abstract {Type} "
+                    + "executeGeneric(VirtualFrame)' and must not throw any checked exceptions.";
+
+    @TypeSystem({int.class, Object[].class})
+    static class ExecuteTypeSystem {
+
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    abstract static class ValidChildNode extends Node {
+        abstract Object execute();
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    @ExpectError(NO_EXECUTE)
+    abstract static class ExecuteThis1 extends Node {
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    @ExpectError(NO_EXECUTE)
+    abstract static class ExecuteThis2 extends Node {
+
+        abstract Object execute() throws UnexpectedResultException;
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    @ExpectError(NO_EXECUTE)
+    abstract static class ExecuteThis3 extends Node {
+
+        abstract int execute() throws UnexpectedResultException;
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    abstract static class ExecuteThis4 extends Node {
+
+        protected abstract Object execute();
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    abstract static class ExecuteThis5 extends Node {
+
+        public abstract Object execute();
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    @ExpectError(NO_EXECUTE)
+    abstract static class ExecuteThis6 extends Node {
+
+        @SuppressWarnings({"unused", "static-method"})
+        private Object execute() {
+            return 0;
+        }
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    @ExpectError(NO_EXECUTE)
+    abstract static class ExecuteThis7 extends Node {
+
+        @SuppressWarnings("static-method")
+        public final int executeInt() {
+            return 0;
+        }
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    @ExpectError("Multiple accessible and overridable generic execute methods found [executeInt(), executeObject()]. Remove all but one or mark all but one as final.")
+    abstract static class ExecuteThis8 extends Node {
+
+        abstract int executeInt();
+
+        abstract Object executeObject();
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+
+    }
+
+    @TypeSystemReference(ExecuteTypeSystem.class)
+    @NodeChild(value = "a", type = ValidChildNode.class)
+    abstract static class ExecuteThis9 extends Node {
+
+        abstract int executeInt();
+
+        // disambiguate executeObject
+        final Object executeObject() {
+            return executeInt();
+        }
+
+        @Specialization
+        int doInt(int a) {
+            return a;
+        }
+    }
+
+}
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeBaseFactory.java	Mon Jan 05 01:31:07 2015 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeBaseFactory.java	Mon Jan 05 01:31:08 2015 +0100
@@ -1170,7 +1170,7 @@
     }
 
     private ExecutableTypeData resolveExecutableType(NodeExecutionData execution, TypeData type) {
-        ExecutableTypeData targetExecutable = execution.getChild().findExecutableType(context, type);
+        ExecutableTypeData targetExecutable = execution.getChild().findExecutableType(type);
         if (targetExecutable == null) {
             targetExecutable = execution.getChild().findAnyGenericExecutableType(context);
         }
@@ -1343,14 +1343,14 @@
                 builder.startElseBlock();
             }
 
-            ExecutableTypeData implictExecutableTypeData = execution.getChild().findExecutableType(context, sourceType);
+            ExecutableTypeData implictExecutableTypeData = execution.getChild().findExecutableType(sourceType);
             if (implictExecutableTypeData == null) {
                 /*
                  * For children with executeWith.size() > 0 an executable type may not exist so use
                  * the generic executable type which is guaranteed to exist. An expect call is
                  * inserted automatically by #createExecuteExpression.
                  */
-                implictExecutableTypeData = execution.getChild().getNodeData().findExecutableType(node.getTypeSystem().getGenericTypeData(), execution.getChild().getExecuteWith().size());
+                implictExecutableTypeData = execution.getChild().getNodeData().findAnyGenericExecutableType(context, execution.getChild().getExecuteWith().size());
             }
 
             ImplicitCastData cast = execution.getChild().getNodeData().getTypeSystem().lookupCast(sourceType, targetParameter.getTypeSystemType());
@@ -1838,12 +1838,12 @@
     /**
      * <pre>
      * variant1 $condition != null
-     *
+     * 
      * $type $name = defaultValue($type);
      * if ($condition) {
      *     $name = $value;
      * }
-     *
+     * 
      * variant2 $condition != null
      * $type $name = $value;
      * </pre>
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeChildData.java	Mon Jan 05 01:31:07 2015 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeChildData.java	Mon Jan 05 01:31:08 2015 +0100
@@ -74,12 +74,8 @@
         this.executeWith = executeWith;
     }
 
-    public ExecutableTypeData findExecutableType(ProcessorContext context, TypeData targetType) {
-        ExecutableTypeData executableType = childNode.findExecutableType(targetType, getExecuteWith().size());
-        if (executableType == null) {
-            executableType = findAnyGenericExecutableType(context);
-        }
-        return executableType;
+    public ExecutableTypeData findExecutableType(TypeData targetType) {
+        return childNode.findExecutableType(targetType, getExecuteWith().size());
     }
 
     public List<ExecutableTypeData> findGenericExecutableTypes(ProcessorContext context) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java	Mon Jan 05 01:31:07 2015 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java	Mon Jan 05 01:31:08 2015 +0100
@@ -133,7 +133,11 @@
             return true;
         }
         for (Parameter parameter : getSignatureParameters()) {
-            ExecutableTypeData type = parameter.getSpecification().getExecution().getChild().findExecutableType(context, parameter.getTypeSystemType());
+            NodeChildData child = parameter.getSpecification().getExecution().getChild();
+            ExecutableTypeData type = child.findExecutableType(parameter.getTypeSystemType());
+            if (type == null) {
+                type = child.findAnyGenericExecutableType(context);
+            }
             if (type.hasUnexpectedValue(context)) {
                 return true;
             }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/ExecutableTypeMethodParser.java	Mon Jan 05 01:31:07 2015 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/ExecutableTypeMethodParser.java	Mon Jan 05 01:31:08 2015 +0100
@@ -81,6 +81,8 @@
             return false;
         } else if (ElementUtils.findAnnotationMirror(getContext().getEnvironment(), method, Specialization.class) != null) {
             return false;
+        } else if (method.getModifiers().contains(Modifier.PRIVATE)) {
+            return false;
         }
         return method.getSimpleName().toString().startsWith("execute");
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Mon Jan 05 01:31:07 2015 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Mon Jan 05 01:31:08 2015 +0100
@@ -148,7 +148,7 @@
             return node; // error sync point
         }
 
-        node.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, node).parse(elements)));
+        initializeExecutableTypes(elements, node);
         initializeChildren(node);
 
         // ensure the processed element has at least one @Specialization annotation.
@@ -374,7 +374,6 @@
                 }
 
                 Element getter = findGetter(elements, name, childType);
-
                 NodeChildData nodeChild = new NodeChildData(type, childMirror, name, childType, originalChildType, getter, cardinality);
 
                 parsedChildren.add(nodeChild);
@@ -527,6 +526,31 @@
         return executions;
     }
 
+    private void initializeExecutableTypes(List<Element> elements, NodeData node) {
+        node.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, node).parse(elements)));
+        List<ExecutableTypeData> genericExecutes = node.getThisExecution().getChild().findGenericExecutableTypes(context);
+
+        List<ExecutableTypeData> overridableGenericExecutes = new ArrayList<>();
+        for (ExecutableTypeData executableTypeData : genericExecutes) {
+            if (!executableTypeData.getMethod().getModifiers().contains(Modifier.FINAL)) {
+                overridableGenericExecutes.add(executableTypeData);
+            }
+        }
+
+        if (overridableGenericExecutes.isEmpty()) {
+            node.addError("No accessible and overridable generic execute method found. Generic execute methods usually have the "
+                            + "signature 'public abstract {Type} executeGeneric(VirtualFrame)' and must not throw any checked exceptions.");
+        }
+
+        if (overridableGenericExecutes.size() > 1) {
+            List<String> methodSignatures = new ArrayList<>();
+            for (ExecutableTypeData type : overridableGenericExecutes) {
+                methodSignatures.add(type.createReferenceName());
+            }
+            node.addWarning("Multiple accessible and overridable generic execute methods found %s. Remove all but one or mark all but one as final.", methodSignatures);
+        }
+    }
+
     private static Map<Integer, List<ExecutableTypeData>> groupExecutableTypes(List<ExecutableTypeData> executableTypes) {
         Map<Integer, List<ExecutableTypeData>> groupedTypes = new TreeMap<>();
         for (ExecutableTypeData type : executableTypes) {
@@ -556,6 +580,9 @@
                 nodeChild.addError("The @%s of the node and the @%s of the @%s does not match. %s != %s. ", TypeSystem.class.getSimpleName(), TypeSystem.class.getSimpleName(),
                                 NodeChild.class.getSimpleName(), ElementUtils.getSimpleName(node.getTypeSystem().getTemplateType()),
                                 ElementUtils.getSimpleName(fieldNodeData.getTypeSystem().getTemplateType()));
+            } else if (nodeChild.findAnyGenericExecutableType(context) == null) {
+                nodeChild.addError("No generic execute method found for child type %s. Generic execute methods usually have the signature 'Object executeGeneric(VirtualFrame)'.",
+                                ElementUtils.getQualifiedName(nodeChild.getNodeType()));
             }
             if (fieldNodeData != null) {
                 List<ExecutableTypeData> types = nodeChild.findGenericExecutableTypes(context);
@@ -983,13 +1010,18 @@
             NodeChildData child = execution.getChild();
             TypeData genericType = null;
             if (types.size() == 1) {
-                ExecutableTypeData executable = child.findExecutableType(context, types.iterator().next());
+                TypeData singleType = types.iterator().next();
+                ExecutableTypeData executable = child.findExecutableType(singleType);
                 if (executable != null && (signatureIndex == 0 || !executable.hasUnexpectedValue(context))) {
-                    genericType = types.iterator().next();
+                    genericType = singleType;
                 }
             }
             if (genericType == null) {
-                genericType = child.findAnyGenericExecutableType(context).getType();
+                ExecutableTypeData type = child.findAnyGenericExecutableType(context);
+                if (type == null) {
+                    throw new AssertionError("No generic type not yet catched by parser.");
+                }
+                genericType = type.getType();
             }
             return genericType.getPrimitiveType();
         }