001/* 002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.nodeinfo.processor; 024 025import static java.util.Collections.*; 026 027import java.io.*; 028import java.util.*; 029import java.util.stream.*; 030 031import javax.annotation.processing.*; 032import javax.lang.model.*; 033import javax.lang.model.element.*; 034import javax.lang.model.util.*; 035import javax.tools.Diagnostic.Kind; 036 037import com.oracle.graal.nodeinfo.*; 038 039@SupportedSourceVersion(SourceVersion.RELEASE_8) 040@SupportedAnnotationTypes({"com.oracle.graal.nodeinfo.NodeInfo"}) 041public class GraphNodeProcessor extends AbstractProcessor { 042 @Override 043 public SourceVersion getSupportedSourceVersion() { 044 return SourceVersion.latest(); 045 } 046 047 /** 048 * Node class currently being processed. 049 */ 050 private Element scope; 051 052 public static boolean isEnclosedIn(Element e, Element scopeElement) { 053 List<Element> elementHierarchy = getElementHierarchy(e); 054 return elementHierarchy.contains(scopeElement); 055 } 056 057 void errorMessage(Element element, String format, Object... args) { 058 message(Kind.ERROR, element, format, args); 059 } 060 061 void message(Kind kind, Element element, String format, Object... args) { 062 if (scope != null && !isEnclosedIn(element, scope)) { 063 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=428357#c1 064 List<Element> elementHierarchy = getElementHierarchy(element); 065 reverse(elementHierarchy); 066 String loc = elementHierarchy.stream().filter(e -> e.getKind() != ElementKind.PACKAGE).map(Object::toString).collect(Collectors.joining(".")); 067 processingEnv.getMessager().printMessage(kind, String.format(loc + ": " + format, args), scope); 068 } else { 069 processingEnv.getMessager().printMessage(kind, String.format(format, args), element); 070 } 071 } 072 073 private static List<Element> getElementHierarchy(Element e) { 074 List<Element> elements = new ArrayList<>(); 075 elements.add(e); 076 077 Element enclosing = e.getEnclosingElement(); 078 while (enclosing != null && enclosing.getKind() != ElementKind.PACKAGE) { 079 elements.add(enclosing); 080 enclosing = enclosing.getEnclosingElement(); 081 } 082 if (enclosing != null) { 083 elements.add(enclosing); 084 } 085 return elements; 086 } 087 088 /** 089 * Bugs in an annotation processor can cause silent failure so try to report any exception 090 * throws as errors. 091 */ 092 private void reportException(Kind kind, Element element, Throwable t) { 093 StringWriter buf = new StringWriter(); 094 t.printStackTrace(new PrintWriter(buf)); 095 buf.toString(); 096 message(kind, element, "Exception thrown during processing: %s", buf.toString()); 097 } 098 099 ProcessingEnvironment getProcessingEnv() { 100 return processingEnv; 101 } 102 103 boolean isNodeType(Element element) { 104 if (element.getKind() != ElementKind.CLASS) { 105 return false; 106 } 107 TypeElement type = (TypeElement) element; 108 Types types = processingEnv.getTypeUtils(); 109 110 while (type != null) { 111 if (type.toString().equals("com.oracle.graal.graph.Node")) { 112 return true; 113 } 114 type = (TypeElement) types.asElement(type.getSuperclass()); 115 } 116 return false; 117 } 118 119 @Override 120 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 121 if (roundEnv.processingOver()) { 122 return false; 123 } 124 125 GraphNodeVerifier verifier = new GraphNodeVerifier(this); 126 127 for (Element element : roundEnv.getElementsAnnotatedWith(NodeInfo.class)) { 128 scope = element; 129 try { 130 if (!isNodeType(element)) { 131 errorMessage(element, "%s can only be applied to Node subclasses", NodeInfo.class.getSimpleName()); 132 continue; 133 } 134 135 NodeInfo nodeInfo = element.getAnnotation(NodeInfo.class); 136 if (nodeInfo == null) { 137 errorMessage(element, "Cannot get %s annotation from annotated element", NodeInfo.class.getSimpleName()); 138 continue; 139 } 140 141 TypeElement typeElement = (TypeElement) element; 142 143 Set<Modifier> modifiers = typeElement.getModifiers(); 144 if (!modifiers.contains(Modifier.FINAL) && !modifiers.contains(Modifier.ABSTRACT)) { 145 // TODO(thomaswue): Reenable this check. 146 // errorMessage(element, "%s annotated class must be either final or abstract", 147 // NodeInfo.class.getSimpleName()); 148 // continue; 149 } 150 boolean found = false; 151 for (Element e : typeElement.getEnclosedElements()) { 152 if (e.getKind() == ElementKind.FIELD) { 153 if (e.getSimpleName().toString().equals("TYPE")) { 154 found = true; 155 break; 156 } 157 } 158 } 159 if (!found) { 160 errorMessage(element, "%s annotated class must have a field named TYPE", NodeInfo.class.getSimpleName()); 161 } 162 163 if (!typeElement.equals(verifier.Node) && !modifiers.contains(Modifier.ABSTRACT)) { 164 verifier.verify(typeElement); 165 } 166 } catch (ElementException ee) { 167 errorMessage(ee.element, ee.getMessage()); 168 } catch (Throwable t) { 169 reportException(isBug367599(t) ? Kind.NOTE : Kind.ERROR, element, t); 170 } finally { 171 scope = null; 172 } 173 } 174 return false; 175 } 176 177 /** 178 * Determines if a given exception is (most likely) caused by <a 179 * href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599">Bug 367599</a>. 180 */ 181 public static boolean isBug367599(Throwable t) { 182 if (t instanceof FilerException) { 183 for (StackTraceElement ste : t.getStackTrace()) { 184 if (ste.toString().contains("org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.create")) { 185 // See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599 186 return true; 187 } 188 } 189 } 190 if (t.getCause() != null) { 191 return isBug367599(t.getCause()); 192 } 193 return false; 194 } 195}