changeset 14086:b6c6a18b2232

Verify existence of constructor for @NodeIntrinsic methods.
author Roland Schatz <roland.schatz@oracle.com>
date Thu, 06 Mar 2014 13:40:13 +0100
parents af84c0b2e74f
children ce79de7da9ab
files graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/NodeIntrinsicVerifier.java graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/VerifierAnnotationProcessor.java mx/projects
diffstat 3 files changed, 182 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/NodeIntrinsicVerifier.java	Thu Mar 06 13:40:13 2014 +0100
@@ -0,0 +1,179 @@
+/*
+ * 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.graal.replacements.verifier;
+
+import java.lang.annotation.*;
+import java.util.*;
+
+import javax.annotation.processing.*;
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+import javax.tools.Diagnostic.Kind;
+
+import com.oracle.graal.graph.Node.ConstantNodeParameter;
+import com.oracle.graal.graph.Node.InjectedNodeParameter;
+import com.oracle.graal.graph.Node.NodeIntrinsic;
+
+public final class NodeIntrinsicVerifier extends AbstractVerifier {
+
+    private static final String NODE_CLASS_NAME = "value";
+
+    private TypeMirror nodeType() {
+        return env.getElementUtils().getTypeElement("com.oracle.graal.graph.Node").asType();
+    }
+
+    private TypeMirror valueNodeType() {
+        return env.getElementUtils().getTypeElement("com.oracle.graal.nodes.ValueNode").asType();
+    }
+
+    private TypeMirror classType() {
+        return env.getElementUtils().getTypeElement("java.lang.Class").asType();
+    }
+
+    private TypeMirror resolvedJavaTypeType() {
+        return env.getElementUtils().getTypeElement("com.oracle.graal.api.meta.ResolvedJavaType").asType();
+    }
+
+    public NodeIntrinsicVerifier(ProcessingEnvironment env) {
+        super(env);
+    }
+
+    @Override
+    public Class<? extends Annotation> getAnnotationClass() {
+        return NodeIntrinsic.class;
+    }
+
+    @Override
+    public void verify(Element element, AnnotationMirror annotation) {
+        if (element.getKind() != ElementKind.METHOD) {
+            assert false : "Element is guaranteed to be a method.";
+            return;
+        }
+
+        ExecutableElement intrinsicMethod = (ExecutableElement) element;
+        if (!intrinsicMethod.getModifiers().contains(Modifier.STATIC)) {
+            env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must be static.", NodeIntrinsic.class.getSimpleName()), element, annotation);
+        }
+
+        TypeMirror nodeClassMirror = resolveAnnotationValue(TypeMirror.class, findAnnotationValue(annotation, NODE_CLASS_NAME));
+        TypeElement nodeClass = (TypeElement) env.getTypeUtils().asElement(nodeClassMirror);
+        if (nodeClass.getSimpleName().contentEquals(NodeIntrinsic.class.getSimpleName())) {
+            // default value
+            Element enclosingElement = intrinsicMethod.getEnclosingElement();
+            while (enclosingElement != null && enclosingElement.getKind() != ElementKind.CLASS) {
+                enclosingElement = enclosingElement.getEnclosingElement();
+            }
+            if (enclosingElement != null) {
+                nodeClass = (TypeElement) enclosingElement;
+            }
+        }
+
+        if (isNodeType(nodeClass)) {
+            TypeMirror[] constructorSignature = constructorSignature(intrinsicMethod);
+            findConstructor(nodeClass, constructorSignature, intrinsicMethod, annotation);
+        } else {
+            env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is not a Node subclass.", nodeClass.getSimpleName()), element, annotation);
+        }
+    }
+
+    private boolean isNodeType(TypeElement nodeClass) {
+        return env.getTypeUtils().isSubtype(nodeClass.asType(), nodeType());
+    }
+
+    private TypeMirror[] constructorSignature(ExecutableElement method) {
+        TypeMirror[] parameters = new TypeMirror[method.getParameters().size()];
+        for (int i = 0; i < method.getParameters().size(); i++) {
+            VariableElement parameter = method.getParameters().get(i);
+            if (parameter.getAnnotation(ConstantNodeParameter.class) == null) {
+                parameters[i] = valueNodeType();
+            } else {
+                TypeMirror type = parameter.asType();
+                if (isTypeCompatible(type, classType())) {
+                    type = resolvedJavaTypeType();
+                }
+                parameters[i] = type;
+            }
+        }
+        return parameters;
+    }
+
+    private void findConstructor(TypeElement nodeClass, TypeMirror[] signature, ExecutableElement intrinsicMethod, AnnotationMirror intrinsicAnnotation) {
+        List<ExecutableElement> constructors = ElementFilter.constructorsIn(nodeClass.getEnclosedElements());
+
+        nextConstructor: for (ExecutableElement constructor : constructors) {
+            int sIdx = 0;
+            int cIdx = 0;
+            while (cIdx < constructor.getParameters().size()) {
+                VariableElement parameter = constructor.getParameters().get(cIdx++);
+                if (parameter.getAnnotation(InjectedNodeParameter.class) != null) {
+                    // skip injected parameters
+                    continue;
+                }
+
+                TypeMirror paramType = parameter.asType();
+                if (cIdx == constructor.getParameters().size() && paramType.getKind() == TypeKind.ARRAY) {
+                    // last argument of constructor is varargs, match remaining intrinsic arguments
+                    TypeMirror varargsType = ((ArrayType) paramType).getComponentType();
+                    while (sIdx < signature.length) {
+                        if (!isTypeCompatible(varargsType, signature[sIdx++])) {
+                            continue nextConstructor;
+                        }
+                    }
+                } else if (sIdx >= signature.length) {
+                    // too many arguments in intrinsic method
+                    continue nextConstructor;
+                } else if (!isTypeCompatible(paramType, signature[sIdx++])) {
+                    continue nextConstructor;
+                }
+            }
+
+            if (sIdx == signature.length) {
+                // found
+                return;
+            }
+
+            // too many arguments in constructor
+        }
+
+        // not found
+        env.getMessager().printMessage(Kind.ERROR, "Could not find matching constructor for node intrinsic.", intrinsicMethod, intrinsicAnnotation);
+    }
+
+    private boolean isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType) {
+        TypeMirror original = originalType;
+        TypeMirror substitution = substitutionType;
+        if (needsErasure(original)) {
+            original = env.getTypeUtils().erasure(original);
+        }
+        if (needsErasure(substitution)) {
+            substitution = env.getTypeUtils().erasure(substitution);
+        }
+        return env.getTypeUtils().isSameType(original, substitution);
+    }
+
+    private static boolean needsErasure(TypeMirror typeMirror) {
+        return typeMirror.getKind() != TypeKind.NONE && typeMirror.getKind() != TypeKind.VOID && !typeMirror.getKind().isPrimitive() && typeMirror.getKind() != TypeKind.OTHER &&
+                        typeMirror.getKind() != TypeKind.NULL;
+    }
+}
--- a/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/VerifierAnnotationProcessor.java	Thu Mar 06 10:40:00 2014 +0100
+++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/VerifierAnnotationProcessor.java	Thu Mar 06 13:40:13 2014 +0100
@@ -72,6 +72,7 @@
             verifiers = new ArrayList<>();
             verifiers.add(new ClassSubstitutionVerifier(this.processingEnv));
             verifiers.add(new MethodSubstitutionVerifier(this.processingEnv));
+            verifiers.add(new NodeIntrinsicVerifier(this.processingEnv));
         }
         return verifiers;
     }
--- a/mx/projects	Thu Mar 06 10:40:00 2014 +0100
+++ b/mx/projects	Thu Mar 06 13:40:13 2014 +0100
@@ -338,7 +338,7 @@
 # graal.replacements.verifier
 project@com.oracle.graal.replacements.verifier@subDir=graal
 project@com.oracle.graal.replacements.verifier@sourceDirs=src
-project@com.oracle.graal.replacements.verifier@dependencies=com.oracle.graal.api.replacements
+project@com.oracle.graal.replacements.verifier@dependencies=com.oracle.graal.api.replacements,com.oracle.graal.graph
 project@com.oracle.graal.replacements.verifier@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.replacements.verifier@javaCompliance=1.7
 project@com.oracle.graal.replacements.verifier@workingSets=Graal,Replacements
@@ -349,6 +349,7 @@
 project@com.oracle.graal.nodes@dependencies=com.oracle.graal.graph,com.oracle.graal.api.replacements
 project@com.oracle.graal.nodes@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.nodes@javaCompliance=1.7
+project@com.oracle.graal.nodes@annotationProcessors=com.oracle.graal.replacements.verifier
 project@com.oracle.graal.nodes@workingSets=Graal,Graph
 
 # graal.nodes.test