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