changeset 11187:7a8835ec5e7d

Truffle-DSL: Added new @NodeField and @NodeFields annotation. Which can be used to avoid the cumbersome definition of copy constructors.
author Christian Humer <christian.humer@gmail.com>
date Tue, 30 Jul 2013 17:42:50 +0200
parents 4a9936bb03a4
children ae6b8ec920e2
files graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/NodeField.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/NodeFields.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeFieldData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeMethodParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java
diffstat 8 files changed, 330 insertions(+), 24 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/NodeFieldTest.java	Tue Jul 30 17:42:50 2013 +0200
@@ -0,0 +1,149 @@
+/*
+ * 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.dsl.test;
+
+import static com.oracle.truffle.api.dsl.test.TestHelper.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.*;
+import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.TestContainerFactory.TestContainerContainerFieldFactory;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+
+public class NodeFieldTest {
+
+    @Test
+    public void testIntField() {
+        assertEquals(42, createCallTarget(IntFieldTestNodeFactory.create(42)).call());
+    }
+
+    @NodeField(name = "field", type = int.class)
+    abstract static class IntFieldTestNode extends ValueNode {
+
+        public abstract int getField();
+
+        @Specialization
+        int intField() {
+            return getField();
+        }
+
+    }
+
+    @Test
+    public void testIntFieldNoGetter() {
+        assertEquals(42, createCallTarget(IntFieldNoGetterTestNodeFactory.create(42)).call());
+    }
+
+    @NodeField(name = "field", type = int.class)
+    abstract static class IntFieldNoGetterTestNode extends ValueNode {
+
+        @Specialization
+        int intField(int field) {
+            return field;
+        }
+
+    }
+
+    @Test
+    public void testMultipleFields() {
+        assertEquals(42, createCallTarget(MultipleFieldsTestNodeFactory.create(21, 21)).call());
+    }
+
+    @NodeFields({@NodeField(name = "field0", type = int.class), @NodeField(name = "field1", type = int.class)})
+    abstract static class MultipleFieldsTestNode extends ValueNode {
+
+        public abstract int getField0();
+
+        public abstract int getField1();
+
+        @Specialization
+        int intField() {
+            return getField0() + getField1();
+        }
+
+    }
+
+    @Test
+    public void testStringField() {
+        assertEquals("42", createCallTarget(StringFieldTestNodeFactory.create("42")).call());
+    }
+
+    @NodeField(name = "field", type = String.class)
+    abstract static class StringFieldTestNode extends ValueNode {
+
+        public abstract String getField();
+
+        @Specialization
+        String stringField() {
+            return getField();
+        }
+
+    }
+
+    @Test
+    public void testRewrite() {
+        assertEquals("42", createCallTarget(RewriteTestNodeFactory.create("42")).call());
+    }
+
+    @NodeField(name = "field", type = String.class)
+    abstract static class RewriteTestNode extends ValueNode {
+
+        public abstract String getField();
+
+        @Specialization(order = 1, rewriteOn = RuntimeException.class)
+        String alwaysRewrite() {
+            throw new RuntimeException();
+        }
+
+        @Specialization(order = 2)
+        String returnField() {
+            return getField();
+        }
+    }
+
+    @Test
+    public void testStringContainer() {
+        assertEquals(42, createCallTarget(TestContainerContainerFieldFactory.create(42, "42")).call());
+    }
+
+    @NodeField(name = "field", type = int.class)
+    abstract static class IntContainerNode extends ValueNode {
+
+        public abstract int getField();
+
+    }
+
+    @NodeContainer(IntContainerNode.class)
+    @NodeField(name = "anotherField", type = String.class)
+    abstract static class TestContainer {
+
+        @Specialization
+        static int containerField(int field, String anotherField) {
+            return anotherField.equals("42") ? field : -1;
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/NodeField.java	Tue Jul 30 17:42:50 2013 +0200
@@ -0,0 +1,43 @@
+/*
+ * 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.dsl;
+
+import java.lang.annotation.*;
+
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * A {@link NodeField} element defines a field for the generated {@link Node}. A {@link Node}
+ * contains multiple {@link NodeFields} specified in linear declaration order. The field can be
+ * accessed by declaring an abstract getter named
+ * <code>"get" + firstLetterUpperCase({@link #name()})()</code>.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE})
+public @interface NodeField {
+
+    String name();
+
+    Class<?> type();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/NodeFields.java	Tue Jul 30 17:42:50 2013 +0200
@@ -0,0 +1,41 @@
+/*
+ * 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.dsl;
+
+import java.lang.annotation.*;
+
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * A {@link NodeFields} element defines a field for the generated {@link Node}. A {@link Node}
+ * contains multiple {@link NodeFields} specified in linear declaration order. The field can be
+ * accessed by declaring an abstract getter named
+ * <code>"get" + firstLetterUpperCase({@link #value()})()</code>.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE})
+public @interface NodeFields {
+
+    NodeField[] value() default {};
+
+}
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java	Tue Jul 30 16:12:26 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java	Tue Jul 30 17:42:50 2013 +0200
@@ -55,7 +55,7 @@
             }
         }
         MethodSpec spec = new MethodSpec(new InheritsParameterSpec(getContext(), "child", baseType));
-        addDefaultFieldMethodSpec(method, spec);
+        addDefaultFieldMethodSpec(spec);
         spec.addRequired(new ParameterSpec("castedChild", baseType)).setSignature(true);
         return spec;
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Tue Jul 30 16:12:26 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Tue Jul 30 17:42:50 2013 +0200
@@ -1073,6 +1073,20 @@
                 }
             }
 
+            for (NodeFieldData field : node.getFields()) {
+                if (!field.isGenerated()) {
+                    continue;
+                }
+
+                clazz.add(new CodeVariableElement(modifiers(PROTECTED, FINAL), field.getType(), field.getName()));
+                if (field.getGetter() != null && field.getGetter().getModifiers().contains(Modifier.ABSTRACT)) {
+                    CodeExecutableElement method = CodeExecutableElement.clone(getContext().getEnvironment(), field.getGetter());
+                    method.getModifiers().remove(Modifier.ABSTRACT);
+                    method.createBuilder().startReturn().string("this.").string(field.getName()).end();
+                    clazz.add(method);
+                }
+            }
+
             for (String assumption : node.getAssumptions()) {
                 clazz.add(createAssumptionField(assumption));
             }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeFieldData.java	Tue Jul 30 16:12:26 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeFieldData.java	Tue Jul 30 17:42:50 2013 +0200
@@ -29,27 +29,49 @@
 
 public class NodeFieldData extends MessageContainer {
 
-    private VariableElement variable;
+    private final Element messageElement;
+    private final AnnotationMirror messageAnnotation;
+    private final String name;
+    private final TypeMirror type;
+    private final boolean generated;
+    private ExecutableElement getter;
 
-    public NodeFieldData(VariableElement var) {
-        this.variable = var;
+    public NodeFieldData(Element messageElement, AnnotationMirror messageAnnotation, TypeMirror type, String name, boolean generated) {
+        this.messageElement = messageElement;
+        this.messageAnnotation = messageAnnotation;
+        this.name = name;
+        this.type = type;
+        this.generated = generated;
+    }
+
+    void setGetter(ExecutableElement getter) {
+        this.getter = getter;
     }
 
     @Override
     public Element getMessageElement() {
-        return variable;
+        return messageElement;
+    }
+
+    @Override
+    public AnnotationMirror getMessageAnnotation() {
+        return messageAnnotation;
     }
 
     public String getName() {
-        return variable.getSimpleName().toString();
+        return name;
     }
 
     public TypeMirror getType() {
-        return variable.asType();
+        return type;
     }
 
-    public VariableElement getVariable() {
-        return variable;
+    public boolean isGenerated() {
+        return generated;
+    }
+
+    public ExecutableElement getGetter() {
+        return getter;
     }
 
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeMethodParser.java	Tue Jul 30 16:12:26 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeMethodParser.java	Tue Jul 30 17:42:50 2013 +0200
@@ -79,7 +79,7 @@
 
         addDefaultFrame(methodSpec);
         addDefaultImplicitThis(method, methodSpec);
-        addDefaultFieldMethodSpec(method, methodSpec);
+        addDefaultFieldMethodSpec(methodSpec);
         addDefaultChildren(shortCircuitsEnabled, shortCircuitName, methodSpec);
 
         return methodSpec;
@@ -119,9 +119,9 @@
         }
     }
 
-    protected void addDefaultFieldMethodSpec(ExecutableElement method, MethodSpec methodSpec) {
+    protected void addDefaultFieldMethodSpec(MethodSpec methodSpec) {
         for (NodeFieldData field : getNode().getFields()) {
-            if (!Utils.isFieldAccessible(method, field.getVariable())) {
+            if (getNode().isNodeContainer() || field.getGetter() == null) {
                 ParameterSpec spec = new ParameterSpec(field.getName(), field.getType());
                 spec.setLocal(true);
                 methodSpec.addOptional(spec);
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java	Tue Jul 30 16:12:26 2013 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java	Tue Jul 30 17:42:50 2013 +0200
@@ -277,10 +277,10 @@
         node.setPolymorphicSpecializations(specializations);
     }
 
-    private NodeData parseNodeData(TypeElement templateType, TypeMirror nodeType, List<? extends Element> elements, List<TypeElement> lookupTypes) {
+    private NodeData parseNodeData(TypeElement templateType, TypeMirror nodeType, List<? extends Element> elements, List<TypeElement> typeHierarchy) {
         NodeData nodeData = new NodeData(templateType, templateType.getSimpleName().toString());
 
-        AnnotationMirror typeSystemMirror = findFirstAnnotation(lookupTypes, TypeSystemReference.class);
+        AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class);
         if (typeSystemMirror == null) {
             nodeData.addError("No @%s annotation found in type hierarchy of %s.", TypeSystemReference.class.getSimpleName(), Utils.getQualifiedName(nodeType));
             return nodeData;
@@ -293,7 +293,7 @@
             return nodeData;
         }
 
-        AnnotationMirror polymorphicMirror = findFirstAnnotation(lookupTypes, PolymorphicLimit.class);
+        AnnotationMirror polymorphicMirror = findFirstAnnotation(typeHierarchy, PolymorphicLimit.class);
         if (polymorphicMirror != null) {
             AnnotationValue limitValue = Utils.getAnnotationValue(polymorphicMirror, "value");
             int polymorphicLimit = Utils.getAnnotationValue(Integer.class, polymorphicMirror, "value");
@@ -304,8 +304,8 @@
         }
 
         List<String> assumptionsList = new ArrayList<>();
-        for (int i = lookupTypes.size() - 1; i >= 0; i--) {
-            TypeElement type = lookupTypes.get(i);
+        for (int i = typeHierarchy.size() - 1; i >= 0; i--) {
+            TypeElement type = typeHierarchy.get(i);
             AnnotationMirror assumptions = Utils.findAnnotationMirror(context.getEnvironment(), type, NodeAssumptions.class);
             if (assumptions != null) {
                 List<String> assumptionStrings = Utils.getAnnotationValueList(String.class, assumptions, "value");
@@ -317,35 +317,66 @@
                 }
             }
         }
-        AnnotationMirror nodeInfoMirror = findFirstAnnotation(lookupTypes, NodeInfo.class);
+        AnnotationMirror nodeInfoMirror = findFirstAnnotation(typeHierarchy, NodeInfo.class);
         if (nodeInfoMirror != null) {
             nodeData.setShortName(Utils.getAnnotationValue(String.class, nodeInfoMirror, "shortName"));
         }
 
         nodeData.setAssumptions(new ArrayList<>(assumptionsList));
         nodeData.setNodeType(nodeType);
-        AnnotationMirror nodeContainer = findFirstAnnotation(lookupTypes, NodeContainer.class);
+        AnnotationMirror nodeContainer = findFirstAnnotation(typeHierarchy, NodeContainer.class);
         nodeData.setNodeContainer(nodeContainer != null);
         nodeData.setTypeSystem(typeSystem);
-        nodeData.setFields(parseFields(elements));
+        nodeData.setFields(parseFields(typeHierarchy, elements));
         parsedNodes.put(Utils.getQualifiedName(templateType), nodeData);
         // parseChildren invokes cyclic parsing.
-        nodeData.setChildren(parseChildren(elements, lookupTypes));
+        nodeData.setChildren(parseChildren(elements, typeHierarchy));
         nodeData.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements)));
 
         return nodeData;
     }
 
-    private static List<NodeFieldData> parseFields(List<? extends Element> elements) {
+    private List<NodeFieldData> parseFields(List<TypeElement> typeHierarchy, List<? extends Element> elements) {
+        Set<String> names = new HashSet<>();
+
         List<NodeFieldData> fields = new ArrayList<>();
         for (VariableElement field : ElementFilter.fieldsIn(elements)) {
             if (field.getModifiers().contains(Modifier.STATIC)) {
                 continue;
             }
             if (field.getModifiers().contains(Modifier.PUBLIC) || field.getModifiers().contains(Modifier.PROTECTED)) {
-                fields.add(new NodeFieldData(field));
+                String name = field.getSimpleName().toString();
+                fields.add(new NodeFieldData(field, null, field.asType(), name, false));
+                names.add(name);
             }
         }
+
+        List<TypeElement> reversedTypeHierarchy = new ArrayList<>(typeHierarchy);
+        Collections.reverse(reversedTypeHierarchy);
+        for (TypeElement typeElement : reversedTypeHierarchy) {
+            AnnotationMirror nodeChildrenMirror = Utils.findAnnotationMirror(processingEnv, typeElement, NodeFields.class);
+            List<AnnotationMirror> children = Utils.collectAnnotations(context, nodeChildrenMirror, "value", typeElement, NodeField.class);
+
+            for (AnnotationMirror mirror : children) {
+                String name = Utils.firstLetterLowerCase(Utils.getAnnotationValue(String.class, mirror, "name"));
+                TypeMirror type = Utils.getAnnotationValue(TypeMirror.class, mirror, "type");
+
+                NodeFieldData field = new NodeFieldData(typeElement, mirror, type, name, true);
+                if (name.isEmpty()) {
+                    field.addError(Utils.getAnnotationValue(mirror, "name"), "Field name cannot be empty.");
+                } else if (names.contains(name)) {
+                    field.addError(Utils.getAnnotationValue(mirror, "name"), "Duplicate field name '%s'.", name);
+                }
+                names.add(name);
+
+                fields.add(field);
+            }
+        }
+
+        for (NodeFieldData nodeFieldData : fields) {
+            nodeFieldData.setGetter(findGetter(elements, nodeFieldData.getName(), nodeFieldData.getType()));
+        }
+
         return fields;
     }
 
@@ -975,6 +1006,12 @@
             unusedElements.removeAll(nodeData.getExtensionElements());
         }
 
+        for (NodeFieldData field : nodeData.getFields()) {
+            if (field.getGetter() != null) {
+                unusedElements.remove(field.getGetter());
+            }
+        }
+
         for (NodeChildData child : nodeData.getChildren()) {
             if (child.getAccessElement() != null) {
                 unusedElements.remove(child.getAccessElement());
@@ -1142,7 +1179,7 @@
         }
     }
 
-    private Element findGetter(List<? extends Element> elements, String variableName, TypeMirror type) {
+    private ExecutableElement findGetter(List<? extends Element> elements, String variableName, TypeMirror type) {
         if (type == null) {
             return null;
         }