changeset 16832:13cf9b6b325c

Truffle-DSL: implemented import guards feature.
author Christian Humer <christian.humer@gmail.com>
date Thu, 14 Aug 2014 16:49:18 +0200
parents c3c07046a74b
children 445f6456c4a5
files graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImportGuardsTest.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/ImportGuards.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/AbstractParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java
diffstat 5 files changed, 266 insertions(+), 5 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/ImportGuardsTest.java	Thu Aug 14 16:49:18 2014 +0200
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2012, 2013, 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 org.junit.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.test.ImportGuardsTestFactory.ImportGuards6Factory;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+
+public class ImportGuardsTest {
+
+    @ImportGuards(Imports0.class)
+    @NodeChild("a")
+    static class ImportGuards0 extends ValueNode {
+
+        @Specialization(guards = "staticGuard")
+        int f0(int a) {
+            return a;
+        }
+    }
+
+    @NodeChild("a")
+    @ImportGuards(Imports0.class)
+    static class ImportGuards1 extends ValueNode {
+
+        @ExpectError("No compatible guard with method name 'nonStaticGuard' found. Please note that all signature types of the method guard must be declared in the type system.")
+        @Specialization(guards = "nonStaticGuard")
+        int f1(int a) {
+            return a;
+        }
+
+        @ExpectError("No compatible guard with method name 'protectedGuard' found. Please note that all signature types of the method guard must be declared in the type system.")
+        @Specialization(guards = "protectedGuard")
+        int f2(int a) {
+            return a;
+        }
+
+        @ExpectError("No compatible guard with method name 'packageGuard' found. Please note that all signature types of the method guard must be declared in the type system.")
+        @Specialization(guards = "packageGuard")
+        int f3(int a) {
+            return a;
+        }
+
+        @ExpectError("No compatible guard with method name 'privateGuard' found. Please note that all signature types of the method guard must be declared in the type system.")
+        @Specialization(guards = "privateGuard")
+        int f4(int a) {
+            return a;
+        }
+    }
+
+    public static class Imports0 {
+        public static boolean staticGuard(int a) {
+            return a == 0;
+        }
+
+        public boolean nonStaticGuard(int a) {
+            return a == 0;
+        }
+
+        protected static boolean protectedGuard(int a) {
+            return a == 0;
+        }
+
+        static boolean packageGuard(int a) {
+            return a == 0;
+        }
+
+        @SuppressWarnings("unused")
+        private static boolean privateGuard(int a) {
+            return a == 0;
+        }
+
+    }
+
+    @ExpectError("The specified import guard class 'com.oracle.truffle.api.dsl.test.ImportGuardsTest.Imports1' must be public.")
+    @NodeChild("a")
+    @ImportGuards(Imports1.class)
+    static class ImportGuards2 extends ValueNode {
+
+        int do1(int a) {
+            return a;
+        }
+    }
+
+    static class Imports1 {
+
+    }
+
+    @ExpectError("The specified import guard class 'com.oracle.truffle.api.dsl.test.ImportGuardsTest.Imports2' must be public.")
+    @NodeChild("a")
+    @ImportGuards(Imports2.class)
+    static class ImportGuards3 extends ValueNode {
+
+        int do1(int a) {
+            return a;
+        }
+    }
+
+    @ExpectError("The specified import guard class 'boolean' is not a declared type.")
+    @NodeChild("a")
+    @ImportGuards(boolean.class)
+    static class ImportGuards4 extends ValueNode {
+
+        int do1(int a) {
+            return a;
+        }
+    }
+
+    private static class Imports2 {
+
+    }
+
+    @ExpectError("At least import guard classes must be specified.")
+    @NodeChild("a")
+    @ImportGuards({})
+    static class ImportGuards5 extends ValueNode {
+
+        int do1(int a) {
+            return a;
+        }
+    }
+
+    @Test
+    public void testImportGuards6() {
+        // should use the guar declared in the node instead of the imported one.
+        assertRuns(ImportGuards6Factory.getInstance(), //
+                        array(1, 1), //
+                        array(1, 1));
+    }
+
+    @ImportGuards(Imports0.class)
+    @NodeChild("a")
+    static class ImportGuards6 extends ValueNode {
+
+        static boolean staticGuard(int a) {
+            return a == 1;
+        }
+
+        @Specialization(guards = "staticGuard")
+        int f0(int a) {
+            return a;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/ImportGuards.java	Thu Aug 14 16:49:18 2014 +0200
@@ -0,0 +1,42 @@
+/*
+ * 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.  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.dsl;
+
+import java.lang.annotation.*;
+
+/**
+ * Imports all public static methods usable as guards for {@link Specialization} annotations to the
+ * current class. Using this annotation common guards can be shared across nodes. Imported guards
+ * are derived from super classes. Guards declared in the node type hierarchy are always preferred
+ * to imported guards. Imported guards for a more concrete type are preferred to guards imported in
+ * the base class.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface ImportGuards {
+
+    Class<?>[] value();
+
+}
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java	Thu Aug 14 15:02:17 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java	Thu Aug 14 16:49:18 2014 +0200
@@ -35,11 +35,11 @@
     private final List<Message> messages = new ArrayList<>();
 
     public final void addWarning(String text, Object... params) {
-        getMessages().add(new Message(null, this, String.format(text, params), Kind.WARNING));
+        getMessages().add(new Message(null, null, this, String.format(text, params), Kind.WARNING));
     }
 
     public final void addWarning(AnnotationValue value, String text, Object... params) {
-        getMessages().add(new Message(value, this, String.format(text, params), Kind.WARNING));
+        getMessages().add(new Message(null, value, this, String.format(text, params), Kind.WARNING));
     }
 
     public final void addError(String text, Object... params) {
@@ -47,7 +47,11 @@
     }
 
     public final void addError(AnnotationValue value, String text, Object... params) {
-        getMessages().add(new Message(value, this, String.format(text, params), Kind.ERROR));
+        getMessages().add(new Message(null, value, this, String.format(text, params), Kind.ERROR));
+    }
+
+    public final void addError(AnnotationMirror mirror, AnnotationValue value, String text, Object... params) {
+        getMessages().add(new Message(mirror, value, this, String.format(text, params), Kind.ERROR));
     }
 
     protected List<MessageContainer> findChildContainers() {
@@ -137,6 +141,9 @@
         if (message.getAnnotationValue() != null) {
             messageValue = message.getAnnotationValue();
         }
+        if (message.getAnnotationMirror() != null) {
+            messageAnnotation = message.getAnnotationMirror();
+        }
 
         String text = message.getText();
 
@@ -224,17 +231,23 @@
     public static final class Message {
 
         private final MessageContainer originalContainer;
+        private final AnnotationMirror annotationMirror;
         private final AnnotationValue annotationValue;
         private final String text;
         private final Kind kind;
 
-        public Message(AnnotationValue annotationValue, MessageContainer originalContainer, String text, Kind kind) {
+        public Message(AnnotationMirror annotationMirror, AnnotationValue annotationValue, MessageContainer originalContainer, String text, Kind kind) {
+            this.annotationMirror = annotationMirror;
             this.annotationValue = annotationValue;
             this.originalContainer = originalContainer;
             this.text = text;
             this.kind = kind;
         }
 
+        public AnnotationMirror getAnnotationMirror() {
+            return annotationMirror;
+        }
+
         public AnnotationValue getAnnotationValue() {
             return annotationValue;
         }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/AbstractParser.java	Thu Aug 14 15:02:17 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/AbstractParser.java	Thu Aug 14 16:49:18 2014 +0200
@@ -83,7 +83,7 @@
                 // redirect message
                 MessageContainer original = message.getOriginalContainer();
                 String text = wrapText(original.getMessageElement(), original.getMessageAnnotation(), message.getText());
-                Message redirectedMessage = new Message(null, baseContainer, text, message.getKind());
+                Message redirectedMessage = new Message(null, null, baseContainer, text, message.getKind());
                 model.getMessages().remove(i);
                 baseContainer.getMessages().add(redirectedMessage);
             }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Thu Aug 14 15:02:17 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Thu Aug 14 16:49:18 2014 +0200
@@ -125,6 +125,7 @@
         return node;
     }
 
+    @SuppressWarnings("unchecked")
     private NodeData parseNode(TypeElement originalTemplateType) {
         // reloading the type elements is needed for ecj
         TypeElement templateType = ElementUtils.fromTypeMirror(context.reloadTypeElement(originalTemplateType));
@@ -141,6 +142,9 @@
         List<? extends Element> elements = CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType);
 
         NodeData node = parseNodeData(templateType, elements, lookupTypes);
+
+        parseImportGuards(node, lookupTypes, (List<Element>) elements);
+
         if (node.hasErrors()) {
             return node; // error sync point
         }
@@ -169,6 +173,40 @@
         return node;
     }
 
+    private void parseImportGuards(NodeData node, List<TypeElement> lookupTypes, List<Element> elements) {
+        for (TypeElement lookupType : lookupTypes) {
+            AnnotationMirror importAnnotation = ElementUtils.findAnnotationMirror(processingEnv, lookupType, ImportGuards.class);
+            if (importAnnotation == null) {
+                continue;
+            }
+            AnnotationValue importClassesValue = ElementUtils.getAnnotationValue(importAnnotation, "value");
+            List<TypeMirror> importClasses = ElementUtils.getAnnotationValueList(TypeMirror.class, importAnnotation, "value");
+            if (importClasses.isEmpty()) {
+                node.addError(importAnnotation, importClassesValue, "At least import guard classes must be specified.");
+                continue;
+            }
+            for (TypeMirror importGuardClass : importClasses) {
+                if (importGuardClass.getKind() != TypeKind.DECLARED) {
+                    node.addError(importAnnotation, importClassesValue, "The specified import guard class '%s' is not a declared type.", ElementUtils.getQualifiedName(importGuardClass));
+                    continue;
+                }
+                TypeElement typeElement = ElementUtils.fromTypeMirror(importGuardClass);
+                if (!typeElement.getModifiers().contains(Modifier.PUBLIC)) {
+                    node.addError(importAnnotation, importClassesValue, "The specified import guard class '%s' must be public.", ElementUtils.getQualifiedName(importGuardClass));
+                    continue;
+                }
+
+                List<? extends ExecutableElement> importMethods = ElementFilter.methodsIn(processingEnv.getElementUtils().getAllMembers(typeElement));
+                for (ExecutableElement importMethod : importMethods) {
+                    if (!importMethod.getModifiers().contains(Modifier.PUBLIC) || !importMethod.getModifiers().contains(Modifier.STATIC)) {
+                        continue;
+                    }
+                    elements.add(importMethod);
+                }
+            }
+        }
+    }
+
     private NodeData parseNodeData(TypeElement templateType, List<? extends Element> elements, List<TypeElement> typeHierarchy) {
         AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class);
         if (typeSystemMirror == null) {