1 /*
   2  * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.nodeinfo.processor;
  24 
  25 import static javax.lang.model.element.Modifier.FINAL;
  26 import static javax.lang.model.element.Modifier.PRIVATE;
  27 import static javax.lang.model.element.Modifier.PROTECTED;
  28 import static javax.lang.model.element.Modifier.PUBLIC;
  29 import static javax.lang.model.element.Modifier.STATIC;
  30 import static javax.lang.model.element.Modifier.TRANSIENT;
  31 
  32 import java.util.List;
  33 import java.util.Set;
  34 
  35 import javax.annotation.processing.ProcessingEnvironment;
  36 import javax.lang.model.element.AnnotationMirror;
  37 import javax.lang.model.element.Element;
  38 import javax.lang.model.element.ElementKind;
  39 import javax.lang.model.element.ExecutableElement;
  40 import javax.lang.model.element.Modifier;
  41 import javax.lang.model.element.TypeElement;
  42 import javax.lang.model.element.VariableElement;
  43 import javax.lang.model.type.TypeMirror;
  44 import javax.lang.model.util.ElementFilter;
  45 import javax.lang.model.util.Elements;
  46 import javax.lang.model.util.Types;
  47 
  48 /**
  49  * Verifies static constraints on nodes.
  50  */
  51 public class GraphNodeVerifier {
  52 
  53     private final GraphNodeProcessor env;
  54     private final Types types;
  55     private final Elements elements;
  56 
  57     // Checkstyle: stop
  58     private final TypeElement Input;
  59     private final TypeElement OptionalInput;
  60     private final TypeElement Successor;
  61 
  62     final TypeElement Node;
  63     private final TypeElement NodeInputList;
  64     private final TypeElement NodeSuccessorList;
  65 
  66     private final TypeElement object;
  67 
  68     // Checkstyle: resume
  69 
  70     public GraphNodeVerifier(GraphNodeProcessor processor) {
  71         this.env = processor;
  72 
  73         this.types = processor.getProcessingEnv().getTypeUtils();
  74         this.elements = processor.getProcessingEnv().getElementUtils();
  75 
  76         this.Input = getTypeElement("org.graalvm.compiler.graph.Node.Input");
  77         this.OptionalInput = getTypeElement("org.graalvm.compiler.graph.Node.OptionalInput");
  78         this.Successor = getTypeElement("org.graalvm.compiler.graph.Node.Successor");
  79         this.Node = getTypeElement("org.graalvm.compiler.graph.Node");
  80         this.NodeInputList = getTypeElement("org.graalvm.compiler.graph.NodeInputList");
  81         this.NodeSuccessorList = getTypeElement("org.graalvm.compiler.graph.NodeSuccessorList");
  82         this.object = getTypeElement("java.lang.Object");
  83     }
  84 
  85     /**
  86      * Returns a type element given a canonical name.
  87      *
  88      * @throw {@link NoClassDefFoundError} if a type element does not exist for {@code name}
  89      */
  90     public TypeElement getTypeElement(String name) {
  91         TypeElement typeElement = elements.getTypeElement(name);
  92         if (typeElement == null) {
  93             throw new NoClassDefFoundError(name);
  94         }
  95         return typeElement;
  96     }
  97 
  98     public TypeElement getTypeElement(Class<?> cls) {
  99         return getTypeElement(cls.getName());
 100     }
 101 
 102     public TypeMirror getType(String name) {
 103         return getTypeElement(name).asType();
 104     }
 105 
 106     public ProcessingEnvironment getProcessingEnv() {
 107         return env.getProcessingEnv();
 108     }
 109 
 110     public boolean isAssignableWithErasure(Element from, Element to) {
 111         TypeMirror fromType = types.erasure(from.asType());
 112         TypeMirror toType = types.erasure(to.asType());
 113         return types.isAssignable(fromType, toType);
 114     }
 115 
 116     private void scanFields(TypeElement node) {
 117         TypeElement currentClazz = node;
 118         do {
 119             for (VariableElement field : ElementFilter.fieldsIn(currentClazz.getEnclosedElements())) {
 120                 Set<Modifier> modifiers = field.getModifiers();
 121                 if (modifiers.contains(STATIC) || modifiers.contains(TRANSIENT)) {
 122                     continue;
 123                 }
 124 
 125                 List<? extends AnnotationMirror> annotations = field.getAnnotationMirrors();
 126 
 127                 boolean isNonOptionalInput = findAnnotationMirror(annotations, Input) != null;
 128                 boolean isOptionalInput = findAnnotationMirror(annotations, OptionalInput) != null;
 129                 boolean isSuccessor = findAnnotationMirror(annotations, Successor) != null;
 130 
 131                 if (isNonOptionalInput || isOptionalInput) {
 132                     if (findAnnotationMirror(annotations, Successor) != null) {
 133                         throw new ElementException(field, "Field cannot be both input and successor");
 134                     } else if (isNonOptionalInput && isOptionalInput) {
 135                         throw new ElementException(field, "Inputs must be either optional or non-optional");
 136                     } else if (isAssignableWithErasure(field, NodeInputList)) {
 137                         if (modifiers.contains(FINAL)) {
 138                             throw new ElementException(field, "Input list field must not be final");
 139                         }
 140                         if (modifiers.contains(PUBLIC)) {
 141                             throw new ElementException(field, "Input list field must not be public");
 142                         }
 143                     } else {
 144                         if (!isAssignableWithErasure(field, Node) && field.getKind() == ElementKind.INTERFACE) {
 145                             throw new ElementException(field, "Input field type must be an interface or assignable to Node");
 146                         }
 147                         if (modifiers.contains(FINAL)) {
 148                             throw new ElementException(field, "Input field must not be final");
 149                         }
 150                         if (modifiers.contains(PUBLIC)) {
 151                             throw new ElementException(field, "Input field must not be public");
 152                         }
 153                     }
 154                 } else if (isSuccessor) {
 155                     if (isAssignableWithErasure(field, NodeSuccessorList)) {
 156                         if (modifiers.contains(FINAL)) {
 157                             throw new ElementException(field, "Successor list field must not be final");
 158                         }
 159                         if (modifiers.contains(PUBLIC)) {
 160                             throw new ElementException(field, "Successor list field must not be public");
 161                         }
 162                     } else {
 163                         if (!isAssignableWithErasure(field, Node)) {
 164                             throw new ElementException(field, "Successor field must be a Node type");
 165                         }
 166                         if (modifiers.contains(FINAL)) {
 167                             throw new ElementException(field, "Successor field must not be final");
 168                         }
 169                         if (modifiers.contains(PUBLIC)) {
 170                             throw new ElementException(field, "Successor field must not be public");
 171                         }
 172                     }
 173 
 174                 } else {
 175                     if (isAssignableWithErasure(field, Node) && !field.getSimpleName().contentEquals("Null")) {
 176                         throw new ElementException(field, "Node field must be annotated with @" + Input.getSimpleName() + ", @" + OptionalInput.getSimpleName() + " or @" + Successor.getSimpleName());
 177                     }
 178                     if (isAssignableWithErasure(field, NodeInputList)) {
 179                         throw new ElementException(field, "NodeInputList field must be annotated with @" + Input.getSimpleName() + " or @" + OptionalInput.getSimpleName());
 180                     }
 181                     if (isAssignableWithErasure(field, NodeSuccessorList)) {
 182                         throw new ElementException(field, "NodeSuccessorList field must be annotated with @" + Successor.getSimpleName());
 183                     }
 184                     if (modifiers.contains(PUBLIC) && !modifiers.contains(FINAL)) {
 185                         throw new ElementException(field, "Data field must be final if public");
 186                     }
 187                 }
 188             }
 189             currentClazz = getSuperType(currentClazz);
 190         } while (!isObject(getSuperType(currentClazz).asType()));
 191     }
 192 
 193     private AnnotationMirror findAnnotationMirror(List<? extends AnnotationMirror> mirrors, TypeElement expectedAnnotationType) {
 194         for (AnnotationMirror mirror : mirrors) {
 195             if (sameType(mirror.getAnnotationType(), expectedAnnotationType.asType())) {
 196                 return mirror;
 197             }
 198         }
 199         return null;
 200     }
 201 
 202     private boolean isObject(TypeMirror type) {
 203         return sameType(object.asType(), type);
 204     }
 205 
 206     private boolean sameType(TypeMirror type1, TypeMirror type2) {
 207         return env.getProcessingEnv().getTypeUtils().isSameType(type1, type2);
 208     }
 209 
 210     private TypeElement getSuperType(TypeElement element) {
 211         if (element.getSuperclass() != null) {
 212             return (TypeElement) env.getProcessingEnv().getTypeUtils().asElement(element.getSuperclass());
 213         }
 214         return null;
 215     }
 216 
 217     void verify(TypeElement node) {
 218         scanFields(node);
 219 
 220         boolean foundValidConstructor = false;
 221         for (ExecutableElement constructor : ElementFilter.constructorsIn(node.getEnclosedElements())) {
 222             if (constructor.getModifiers().contains(PRIVATE)) {
 223                 continue;
 224             } else if (!constructor.getModifiers().contains(PUBLIC) && !constructor.getModifiers().contains(PROTECTED)) {
 225                 throw new ElementException(constructor, "Node class constructor must be public or protected");
 226             }
 227 
 228             foundValidConstructor = true;
 229         }
 230 
 231         if (!foundValidConstructor) {
 232             throw new ElementException(node, "Node class must have at least one protected constructor");
 233         }
 234     }
 235 }