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 }