1 /*
2  * Copyright (c) 2014, 2018, 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 
24 
25 package org.graalvm.compiler.nodeinfo.processor;
26 
27 import static javax.lang.model.element.Modifier.FINAL;
28 import static javax.lang.model.element.Modifier.PRIVATE;
29 import static javax.lang.model.element.Modifier.PROTECTED;
30 import static javax.lang.model.element.Modifier.PUBLIC;
31 import static javax.lang.model.element.Modifier.STATIC;
32 import static javax.lang.model.element.Modifier.TRANSIENT;
33 
34 import java.util.List;
35 import java.util.Set;
36 
37 import javax.lang.model.element.AnnotationMirror;
38 import javax.lang.model.element.Element;
39 import javax.lang.model.element.ElementKind;
40 import javax.lang.model.element.ExecutableElement;
41 import javax.lang.model.element.Modifier;
42 import javax.lang.model.element.TypeElement;
43 import javax.lang.model.element.VariableElement;
44 import javax.lang.model.type.TypeMirror;
45 import javax.lang.model.util.ElementFilter;
46 import javax.lang.model.util.Types;
47 
48 import org.graalvm.compiler.processor.AbstractProcessor;
49 
50 /**
51  * Verifies static constraints on nodes.
52  */
53 public class GraphNodeVerifier {
54 
55     private final AbstractProcessor processor;
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 
GraphNodeVerifier(AbstractProcessor processor)70     public GraphNodeVerifier(AbstractProcessor processor) {
71         this.processor = processor;
72         this.Input = getTypeElement("org.graalvm.compiler.graph.Node.Input");
73         this.OptionalInput = getTypeElement("org.graalvm.compiler.graph.Node.OptionalInput");
74         this.Successor = getTypeElement("org.graalvm.compiler.graph.Node.Successor");
75         this.Node = getTypeElement("org.graalvm.compiler.graph.Node");
76         this.NodeInputList = getTypeElement("org.graalvm.compiler.graph.NodeInputList");
77         this.NodeSuccessorList = getTypeElement("org.graalvm.compiler.graph.NodeSuccessorList");
78         this.object = getTypeElement("java.lang.Object");
79     }
80 
81     /**
82      * Returns a type element given a canonical name.
83      *
84      * @throw {@link NoClassDefFoundError} if a type element does not exist for {@code name}
85      */
getTypeElement(String name)86     public TypeElement getTypeElement(String name) {
87         return processor.getTypeElement(name);
88     }
89 
getTypeElement(Class<?> cls)90     public TypeElement getTypeElement(Class<?> cls) {
91         return getTypeElement(cls.getName());
92     }
93 
getType(String name)94     public TypeMirror getType(String name) {
95         return getTypeElement(name).asType();
96     }
97 
isAssignableWithErasure(Element from, Element to)98     public boolean isAssignableWithErasure(Element from, Element to) {
99         Types types = processor.env().getTypeUtils();
100         TypeMirror fromType = types.erasure(from.asType());
101         TypeMirror toType = types.erasure(to.asType());
102         return types.isAssignable(fromType, toType);
103     }
104 
scanFields(TypeElement node)105     private void scanFields(TypeElement node) {
106         TypeElement currentClazz = node;
107         do {
108             for (VariableElement field : ElementFilter.fieldsIn(currentClazz.getEnclosedElements())) {
109                 Set<Modifier> modifiers = field.getModifiers();
110                 if (modifiers.contains(STATIC) || modifiers.contains(TRANSIENT)) {
111                     continue;
112                 }
113 
114                 List<? extends AnnotationMirror> annotations = field.getAnnotationMirrors();
115 
116                 boolean isNonOptionalInput = findAnnotationMirror(annotations, Input) != null;
117                 boolean isOptionalInput = findAnnotationMirror(annotations, OptionalInput) != null;
118                 boolean isSuccessor = findAnnotationMirror(annotations, Successor) != null;
119 
120                 if (isNonOptionalInput || isOptionalInput) {
121                     if (findAnnotationMirror(annotations, Successor) != null) {
122                         throw new ElementException(field, "Field cannot be both input and successor");
123                     } else if (isNonOptionalInput && isOptionalInput) {
124                         throw new ElementException(field, "Inputs must be either optional or non-optional");
125                     } else if (isAssignableWithErasure(field, NodeInputList)) {
126                         if (modifiers.contains(FINAL)) {
127                             throw new ElementException(field, "Input list field must not be final");
128                         }
129                         if (modifiers.contains(PUBLIC)) {
130                             throw new ElementException(field, "Input list field must not be public");
131                         }
132                     } else {
133                         if (!isAssignableWithErasure(field, Node) && field.getKind() == ElementKind.INTERFACE) {
134                             throw new ElementException(field, "Input field type must be an interface or assignable to Node");
135                         }
136                         if (modifiers.contains(FINAL)) {
137                             throw new ElementException(field, "Input field must not be final");
138                         }
139                         if (modifiers.contains(PUBLIC)) {
140                             throw new ElementException(field, "Input field must not be public");
141                         }
142                     }
143                 } else if (isSuccessor) {
144                     if (isAssignableWithErasure(field, NodeSuccessorList)) {
145                         if (modifiers.contains(FINAL)) {
146                             throw new ElementException(field, "Successor list field must not be final");
147                         }
148                         if (modifiers.contains(PUBLIC)) {
149                             throw new ElementException(field, "Successor list field must not be public");
150                         }
151                     } else {
152                         if (!isAssignableWithErasure(field, Node)) {
153                             throw new ElementException(field, "Successor field must be a Node type");
154                         }
155                         if (modifiers.contains(FINAL)) {
156                             throw new ElementException(field, "Successor field must not be final");
157                         }
158                         if (modifiers.contains(PUBLIC)) {
159                             throw new ElementException(field, "Successor field must not be public");
160                         }
161                     }
162 
163                 } else {
164                     if (isAssignableWithErasure(field, Node) && !field.getSimpleName().contentEquals("Null")) {
165                         throw new ElementException(field, "Node field must be annotated with @" + Input.getSimpleName() + ", @" + OptionalInput.getSimpleName() + " or @" + Successor.getSimpleName());
166                     }
167                     if (isAssignableWithErasure(field, NodeInputList)) {
168                         throw new ElementException(field, "NodeInputList field must be annotated with @" + Input.getSimpleName() + " or @" + OptionalInput.getSimpleName());
169                     }
170                     if (isAssignableWithErasure(field, NodeSuccessorList)) {
171                         throw new ElementException(field, "NodeSuccessorList field must be annotated with @" + Successor.getSimpleName());
172                     }
173                     if (modifiers.contains(PUBLIC) && !modifiers.contains(FINAL)) {
174                         throw new ElementException(field, "Data field must be final if public");
175                     }
176                 }
177             }
178             currentClazz = getSuperType(currentClazz);
179         } while (!isObject(getSuperType(currentClazz).asType()));
180     }
181 
findAnnotationMirror(List<? extends AnnotationMirror> mirrors, TypeElement expectedAnnotationType)182     private AnnotationMirror findAnnotationMirror(List<? extends AnnotationMirror> mirrors, TypeElement expectedAnnotationType) {
183         for (AnnotationMirror mirror : mirrors) {
184             if (sameType(mirror.getAnnotationType(), expectedAnnotationType.asType())) {
185                 return mirror;
186             }
187         }
188         return null;
189     }
190 
isObject(TypeMirror type)191     private boolean isObject(TypeMirror type) {
192         return sameType(object.asType(), type);
193     }
194 
sameType(TypeMirror type1, TypeMirror type2)195     private boolean sameType(TypeMirror type1, TypeMirror type2) {
196         return processor.env().getTypeUtils().isSameType(type1, type2);
197     }
198 
getSuperType(TypeElement element)199     private TypeElement getSuperType(TypeElement element) {
200         if (element.getSuperclass() != null) {
201             return processor.asTypeElement(element.getSuperclass());
202         }
203         return null;
204     }
205 
verify(TypeElement node)206     void verify(TypeElement node) {
207         scanFields(node);
208 
209         boolean foundValidConstructor = false;
210         for (ExecutableElement constructor : ElementFilter.constructorsIn(node.getEnclosedElements())) {
211             if (constructor.getModifiers().contains(PRIVATE)) {
212                 continue;
213             } else if (!constructor.getModifiers().contains(PUBLIC) && !constructor.getModifiers().contains(PROTECTED)) {
214                 throw new ElementException(constructor, "Node class constructor must be public or protected");
215             }
216 
217             foundValidConstructor = true;
218         }
219 
220         if (!foundValidConstructor) {
221             throw new ElementException(node, "Node class must have at least one protected constructor");
222         }
223     }
224 }
225