| /* |
| * Copyright (c) 2014, 2015, 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 org.graalvm.compiler.nodeinfo.processor; |
| |
| import static javax.lang.model.element.Modifier.FINAL; |
| import static javax.lang.model.element.Modifier.PRIVATE; |
| import static javax.lang.model.element.Modifier.PROTECTED; |
| import static javax.lang.model.element.Modifier.PUBLIC; |
| import static javax.lang.model.element.Modifier.STATIC; |
| import static javax.lang.model.element.Modifier.TRANSIENT; |
| |
| import java.util.List; |
| import java.util.Set; |
| |
| import javax.annotation.processing.ProcessingEnvironment; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.Modifier; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.util.ElementFilter; |
| import javax.lang.model.util.Elements; |
| import javax.lang.model.util.Types; |
| |
| /** |
| * Verifies static constraints on nodes. |
| */ |
| public class GraphNodeVerifier { |
| |
| private final GraphNodeProcessor env; |
| private final Types types; |
| private final Elements elements; |
| |
| // Checkstyle: stop |
| private final TypeElement Input; |
| private final TypeElement OptionalInput; |
| private final TypeElement Successor; |
| |
| final TypeElement Node; |
| private final TypeElement NodeInputList; |
| private final TypeElement NodeSuccessorList; |
| |
| private final TypeElement object; |
| |
| // Checkstyle: resume |
| |
| public GraphNodeVerifier(GraphNodeProcessor processor) { |
| this.env = processor; |
| |
| this.types = processor.getProcessingEnv().getTypeUtils(); |
| this.elements = processor.getProcessingEnv().getElementUtils(); |
| |
| this.Input = getTypeElement("org.graalvm.compiler.graph.Node.Input"); |
| this.OptionalInput = getTypeElement("org.graalvm.compiler.graph.Node.OptionalInput"); |
| this.Successor = getTypeElement("org.graalvm.compiler.graph.Node.Successor"); |
| this.Node = getTypeElement("org.graalvm.compiler.graph.Node"); |
| this.NodeInputList = getTypeElement("org.graalvm.compiler.graph.NodeInputList"); |
| this.NodeSuccessorList = getTypeElement("org.graalvm.compiler.graph.NodeSuccessorList"); |
| this.object = getTypeElement("java.lang.Object"); |
| } |
| |
| /** |
| * Returns a type element given a canonical name. |
| * |
| * @throw {@link NoClassDefFoundError} if a type element does not exist for {@code name} |
| */ |
| public TypeElement getTypeElement(String name) { |
| TypeElement typeElement = elements.getTypeElement(name); |
| if (typeElement == null) { |
| throw new NoClassDefFoundError(name); |
| } |
| return typeElement; |
| } |
| |
| public TypeElement getTypeElement(Class<?> cls) { |
| return getTypeElement(cls.getName()); |
| } |
| |
| public TypeMirror getType(String name) { |
| return getTypeElement(name).asType(); |
| } |
| |
| public ProcessingEnvironment getProcessingEnv() { |
| return env.getProcessingEnv(); |
| } |
| |
| public boolean isAssignableWithErasure(Element from, Element to) { |
| TypeMirror fromType = types.erasure(from.asType()); |
| TypeMirror toType = types.erasure(to.asType()); |
| return types.isAssignable(fromType, toType); |
| } |
| |
| private void scanFields(TypeElement node) { |
| TypeElement currentClazz = node; |
| do { |
| for (VariableElement field : ElementFilter.fieldsIn(currentClazz.getEnclosedElements())) { |
| Set<Modifier> modifiers = field.getModifiers(); |
| if (modifiers.contains(STATIC) || modifiers.contains(TRANSIENT)) { |
| continue; |
| } |
| |
| List<? extends AnnotationMirror> annotations = field.getAnnotationMirrors(); |
| |
| boolean isNonOptionalInput = findAnnotationMirror(annotations, Input) != null; |
| boolean isOptionalInput = findAnnotationMirror(annotations, OptionalInput) != null; |
| boolean isSuccessor = findAnnotationMirror(annotations, Successor) != null; |
| |
| if (isNonOptionalInput || isOptionalInput) { |
| if (findAnnotationMirror(annotations, Successor) != null) { |
| throw new ElementException(field, "Field cannot be both input and successor"); |
| } else if (isNonOptionalInput && isOptionalInput) { |
| throw new ElementException(field, "Inputs must be either optional or non-optional"); |
| } else if (isAssignableWithErasure(field, NodeInputList)) { |
| if (modifiers.contains(FINAL)) { |
| throw new ElementException(field, "Input list field must not be final"); |
| } |
| if (modifiers.contains(PUBLIC)) { |
| throw new ElementException(field, "Input list field must not be public"); |
| } |
| } else { |
| if (!isAssignableWithErasure(field, Node) && field.getKind() == ElementKind.INTERFACE) { |
| throw new ElementException(field, "Input field type must be an interface or assignable to Node"); |
| } |
| if (modifiers.contains(FINAL)) { |
| throw new ElementException(field, "Input field must not be final"); |
| } |
| if (modifiers.contains(PUBLIC)) { |
| throw new ElementException(field, "Input field must not be public"); |
| } |
| } |
| } else if (isSuccessor) { |
| if (isAssignableWithErasure(field, NodeSuccessorList)) { |
| if (modifiers.contains(FINAL)) { |
| throw new ElementException(field, "Successor list field must not be final"); |
| } |
| if (modifiers.contains(PUBLIC)) { |
| throw new ElementException(field, "Successor list field must not be public"); |
| } |
| } else { |
| if (!isAssignableWithErasure(field, Node)) { |
| throw new ElementException(field, "Successor field must be a Node type"); |
| } |
| if (modifiers.contains(FINAL)) { |
| throw new ElementException(field, "Successor field must not be final"); |
| } |
| if (modifiers.contains(PUBLIC)) { |
| throw new ElementException(field, "Successor field must not be public"); |
| } |
| } |
| |
| } else { |
| if (isAssignableWithErasure(field, Node) && !field.getSimpleName().contentEquals("Null")) { |
| throw new ElementException(field, "Node field must be annotated with @" + Input.getSimpleName() + ", @" + OptionalInput.getSimpleName() + " or @" + Successor.getSimpleName()); |
| } |
| if (isAssignableWithErasure(field, NodeInputList)) { |
| throw new ElementException(field, "NodeInputList field must be annotated with @" + Input.getSimpleName() + " or @" + OptionalInput.getSimpleName()); |
| } |
| if (isAssignableWithErasure(field, NodeSuccessorList)) { |
| throw new ElementException(field, "NodeSuccessorList field must be annotated with @" + Successor.getSimpleName()); |
| } |
| if (modifiers.contains(PUBLIC) && !modifiers.contains(FINAL)) { |
| throw new ElementException(field, "Data field must be final if public"); |
| } |
| } |
| } |
| currentClazz = getSuperType(currentClazz); |
| } while (!isObject(getSuperType(currentClazz).asType())); |
| } |
| |
| private AnnotationMirror findAnnotationMirror(List<? extends AnnotationMirror> mirrors, TypeElement expectedAnnotationType) { |
| for (AnnotationMirror mirror : mirrors) { |
| if (sameType(mirror.getAnnotationType(), expectedAnnotationType.asType())) { |
| return mirror; |
| } |
| } |
| return null; |
| } |
| |
| private boolean isObject(TypeMirror type) { |
| return sameType(object.asType(), type); |
| } |
| |
| private boolean sameType(TypeMirror type1, TypeMirror type2) { |
| return env.getProcessingEnv().getTypeUtils().isSameType(type1, type2); |
| } |
| |
| private TypeElement getSuperType(TypeElement element) { |
| if (element.getSuperclass() != null) { |
| return (TypeElement) env.getProcessingEnv().getTypeUtils().asElement(element.getSuperclass()); |
| } |
| return null; |
| } |
| |
| void verify(TypeElement node) { |
| scanFields(node); |
| |
| boolean foundValidConstructor = false; |
| for (ExecutableElement constructor : ElementFilter.constructorsIn(node.getEnclosedElements())) { |
| if (constructor.getModifiers().contains(PRIVATE)) { |
| continue; |
| } else if (!constructor.getModifiers().contains(PUBLIC) && !constructor.getModifiers().contains(PROTECTED)) { |
| throw new ElementException(constructor, "Node class constructor must be public or protected"); |
| } |
| |
| foundValidConstructor = true; |
| } |
| |
| if (!foundValidConstructor) { |
| throw new ElementException(node, "Node class must have at least one protected constructor"); |
| } |
| } |
| } |