# HG changeset patch # User Christian Humer # Date 1363885412 -3600 # Node ID 467b41309cdaedc1001dfd719bfc7d466ecb554c # Parent 18e2856d1993b12f5c0a40e8cb55c69ec6918714 Added annotation processor for verifing @ClassSubstitution and @MethodSubstitution annotation at compile time. diff -r 18e2856d1993 -r 467b41309cda graal/com.oracle.graal.replacements.verifier/src/META-INF/services/javax.annotation.processing.Processor --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/META-INF/services/javax.annotation.processing.Processor Thu Mar 21 18:03:32 2013 +0100 @@ -0,0 +1,1 @@ +com.oracle.graal.replacements.verifier.VerifierAnnotationProcessor diff -r 18e2856d1993 -r 467b41309cda graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/APHotSpotSignature.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/APHotSpotSignature.java Thu Mar 21 18:03:32 2013 +0100 @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2013, 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 com.oracle.graal.replacements.verifier; + +import java.util.*; + +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.tools.Diagnostic.*; + +/** + * Pretty much copied from HotSpotSignature but using a different method for resolving types. This + * class should be rewritten, its just a quick hack to get signatures working. + */ +final class APHotSpotSignature { + + private final List arguments = new ArrayList<>(); + private final String returnType; + private final String originalString; + private TypeMirror[] argumentTypes; + private TypeMirror returnTypeCache; + + APHotSpotSignature(String signature) { + assert signature.length() > 0; + this.originalString = signature; + + if (signature.charAt(0) == '(') { + int cur = 1; + while (cur < signature.length() && signature.charAt(cur) != ')') { + int nextCur = parseSignature(signature, cur); + arguments.add(signature.substring(cur, nextCur)); + cur = nextCur; + } + + cur++; + int nextCur = parseSignature(signature, cur); + returnType = signature.substring(cur, nextCur); + if (nextCur != signature.length()) { + throw new RuntimeException("Invalid trailing characters."); + } + } else { + returnType = null; + } + } + + private static int parseSignature(String signature, int start) { + int cur = start; + char first; + do { + first = signature.charAt(cur++); + } while (first == '['); + + switch (first) { + case 'L': + while (signature.charAt(cur) != ';') { + cur++; + } + cur++; + break; + case 'V': + case 'I': + case 'B': + case 'C': + case 'D': + case 'F': + case 'J': + case 'S': + case 'Z': + break; + default: + throw new RuntimeException("Invalid character at index " + cur + " in signature: " + signature); + } + return cur; + } + + public int getParameterCount(boolean withReceiver) { + return arguments.size() + (withReceiver ? 1 : 0); + } + + public TypeMirror getParameterType(ProcessingEnvironment env, int index) { + if (argumentTypes == null) { + argumentTypes = new TypeMirror[arguments.size()]; + } + TypeMirror type = argumentTypes[index]; + if (arguments.get(index) == null) { + throw new RuntimeException(String.format("Invalid argument at index %s.", index)); + } + + if (type == null) { + argumentTypes[index] = lookupType(env, arguments.get(index)); + } + return argumentTypes[index]; + } + + private static TypeMirror lookupType(ProcessingEnvironment env, String binaryName) { + if (binaryName.length() == 1) { + TypeKind kind = fromPrimitiveOrVoidTypeChar(binaryName.charAt(0)); + if (kind.isPrimitive()) { + return env.getTypeUtils().getPrimitiveType(kind); + } else if (kind == TypeKind.VOID) { + return env.getTypeUtils().getNoType(kind); + } + } + + String canonicalName = binaryName; + if (canonicalName.startsWith("L") && canonicalName.endsWith(";")) { + canonicalName = canonicalName.substring(1, canonicalName.length() - 1); + } + env.getMessager().printMessage(Kind.ERROR, canonicalName); + + int arrayDims = 0; + while (canonicalName.startsWith("[")) { + canonicalName = canonicalName.substring(1, canonicalName.length()); + arrayDims++; + } + + canonicalName = canonicalName.replaceAll("/", "."); + TypeElement typeElement = env.getElementUtils().getTypeElement(canonicalName); + if (typeElement == null) { + throw new RuntimeException(String.format("Type with name %s not found.", canonicalName)); + } + TypeMirror mirror = typeElement.asType(); + for (int i = 0; i < arrayDims; i++) { + mirror = env.getTypeUtils().getArrayType(mirror); + } + return mirror; + } + + /** + * Returns the kind from the character describing a primitive or void. + * + * @param ch the character + * @return the kind + */ + public static TypeKind fromPrimitiveOrVoidTypeChar(char ch) { + switch (ch) { + case 'Z': + return TypeKind.BOOLEAN; + case 'C': + return TypeKind.CHAR; + case 'F': + return TypeKind.FLOAT; + case 'D': + return TypeKind.DOUBLE; + case 'B': + return TypeKind.BYTE; + case 'S': + return TypeKind.SHORT; + case 'I': + return TypeKind.INT; + case 'J': + return TypeKind.LONG; + case 'V': + return TypeKind.VOID; + } + throw new IllegalArgumentException("unknown primitive or void type character: " + ch); + } + + public TypeMirror getReturnType(ProcessingEnvironment env) { + if (returnTypeCache == null) { + if (returnType == null) { + throw new RuntimeException("Invalid return type."); + } + returnTypeCache = lookupType(env, returnType); + } + return returnTypeCache; + } + + @Override + public String toString() { + return "Signature<" + originalString + ">"; + } +} diff -r 18e2856d1993 -r 467b41309cda graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/AbstractVerifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/AbstractVerifier.java Thu Mar 21 18:03:32 2013 +0100 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013, 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 com.oracle.graal.replacements.verifier; + +import java.lang.annotation.*; + +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; + +public abstract class AbstractVerifier { + + protected final ProcessingEnvironment env; + + public AbstractVerifier(ProcessingEnvironment env) { + this.env = env; + } + + public abstract void verify(Element element, AnnotationMirror annotation); + + public abstract Class getAnnotationClass(); + + @SuppressWarnings("unchecked") + protected static T resolveAnnotationValue(Class expectedType, AnnotationValue value) { + if (value == null) { + throw new NullPointerException("Value must not be null."); + } + Object unboxedValue = value.getValue(); + if (unboxedValue != null) { + if (expectedType == TypeMirror.class && unboxedValue instanceof String) { + /* + * Happens if type is invalid when using the ECJ compiler. The ECJ does not match + * AP-API specification here. + */ + return null; + } + if (!expectedType.isAssignableFrom(unboxedValue.getClass())) { + throw new ClassCastException(unboxedValue.getClass().getName() + " not assignable from " + expectedType.getName()); + } + } + return (T) unboxedValue; + } + + protected static AnnotationValue findAnnotationValue(AnnotationMirror mirror, String name) { + ExecutableElement valueMethod = null; + for (ExecutableElement method : ElementFilter.methodsIn(mirror.getAnnotationType().asElement().getEnclosedElements())) { + if (method.getSimpleName().toString().equals(name)) { + valueMethod = method; + break; + } + } + + if (valueMethod == null) { + return null; + } + + AnnotationValue value = mirror.getElementValues().get(valueMethod); + if (value == null) { + value = valueMethod.getDefaultValue(); + } + + return value; + } + +} diff -r 18e2856d1993 -r 467b41309cda graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/ClassSubstitutionVerifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/ClassSubstitutionVerifier.java Thu Mar 21 18:03:32 2013 +0100 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013, 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 com.oracle.graal.replacements.verifier; + +import java.lang.annotation.*; + +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.tools.Diagnostic.*; + +import com.oracle.graal.api.replacements.*; + +public final class ClassSubstitutionVerifier extends AbstractVerifier { + + private static final String TYPE_VALUE = "value"; + private static final String STRING_VALUE = "className"; + private static final String OPTIONAL = "optional"; + private static final String STRING_VALUE_DEFAULT = ""; + + public ClassSubstitutionVerifier(ProcessingEnvironment env) { + super(env); + } + + @Override + public Class getAnnotationClass() { + return ClassSubstitution.class; + } + + @Override + public void verify(Element element, AnnotationMirror classSubstitution) { + if (!element.getKind().isClass()) { + assert false : "Element is guaranteed to be a class."; + return; + } + TypeElement type = (TypeElement) element; + + TypeElement substitutionType = resolveOriginalType(env, type, classSubstitution); + if (substitutionType == null) { + return; + } + } + + static TypeElement resolveOriginalType(ProcessingEnvironment env, Element sourceElement, AnnotationMirror classSubstition) { + AnnotationValue typeValue = findAnnotationValue(classSubstition, TYPE_VALUE); + AnnotationValue stringValue = findAnnotationValue(classSubstition, STRING_VALUE); + AnnotationValue optionalValue = findAnnotationValue(classSubstition, OPTIONAL); + + assert typeValue != null && stringValue != null && optionalValue != null; + + TypeMirror type = resolveAnnotationValue(TypeMirror.class, typeValue); + String className = resolveAnnotationValue(String.class, stringValue); + boolean optional = resolveAnnotationValue(Boolean.class, optionalValue); + if (!classSubstition.getAnnotationType().equals(type)) { + if (!className.equals(STRING_VALUE_DEFAULT)) { + String msg = "The usage of value and className is exclusive."; + env.getMessager().printMessage(Kind.ERROR, msg, sourceElement, classSubstition, stringValue); + env.getMessager().printMessage(Kind.ERROR, msg, sourceElement, classSubstition, typeValue); + } + if (type.getKind() != TypeKind.DECLARED) { + env.getMessager().printMessage(Kind.ERROR, "The provided class must be a declared type.", sourceElement, classSubstition, typeValue); + return null; + } + return (TypeElement) ((DeclaredType) type).asElement(); + } + + if (!className.equals(STRING_VALUE_DEFAULT)) { + TypeElement typeElement = env.getElementUtils().getTypeElement(className); + if (typeElement == null && !optional) { + env.getMessager().printMessage(Kind.ERROR, String.format("The class '%s' was not found on the classpath.", stringValue), sourceElement, classSubstition, stringValue); + } + return typeElement; + } + + if (!optional) { + env.getMessager().printMessage(Kind.ERROR, String.format("No value for '%s' or '%s' provided but required.", TYPE_VALUE, STRING_VALUE), sourceElement, classSubstition); + } + + return null; + } + +} diff -r 18e2856d1993 -r 467b41309cda graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/MethodSubstitutionVerifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/MethodSubstitutionVerifier.java Thu Mar 21 18:03:32 2013 +0100 @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2013, 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 com.oracle.graal.replacements.verifier; + +import java.lang.annotation.*; +import java.util.*; + +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; +import javax.tools.Diagnostic.Kind; + +import com.oracle.graal.api.replacements.*; + +public final class MethodSubstitutionVerifier extends AbstractVerifier { + + private static final boolean DEBUG = false; + + private static final String ORIGINAL_METHOD_NAME = "value"; + private static final String ORIGINAL_IS_STATIC = "isStatic"; + private static final String ORIGINAL_SIGNATURE = "signature"; + + private static final String ORIGINAL_METHOD_NAME_DEFAULT = ""; + private static final String ORIGINAL_SIGNATURE_DEFAULT = ""; + + public MethodSubstitutionVerifier(ProcessingEnvironment env) { + super(env); + } + + @Override + public Class getAnnotationClass() { + return MethodSubstitution.class; + } + + @SuppressWarnings("unused") + @Override + public void verify(Element element, AnnotationMirror annotation) { + if (element.getKind() != ElementKind.METHOD) { + assert false : "Element is guaranteed to be a method."; + return; + } + ExecutableElement substitutionMethod = (ExecutableElement) element; + TypeElement substitutionType = findEnclosingClass(substitutionMethod); + assert substitutionType != null; + + AnnotationMirror substitutionClassAnnotation = VerifierAnnotationProcessor.findAnnotationMirror(env, substitutionType.getAnnotationMirrors(), ClassSubstitution.class); + if (substitutionClassAnnotation == null) { + env.getMessager().printMessage(Kind.ERROR, String.format("A @%s annotation is required on the enclosing class.", ClassSubstitution.class.getSimpleName()), element, annotation); + return; + } + boolean optional = resolveAnnotationValue(Boolean.class, findAnnotationValue(substitutionClassAnnotation, "optional")); + if (optional) { + return; + } + + TypeElement originalType = ClassSubstitutionVerifier.resolveOriginalType(env, substitutionType, substitutionClassAnnotation); + if (originalType == null) { + env.getMessager().printMessage(Kind.ERROR, String.format("The @%s annotation is invalid on the enclosing class.", ClassSubstitution.class.getSimpleName()), element, annotation); + return; + } + + if (!substitutionMethod.getModifiers().contains(Modifier.STATIC)) { + env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must be static.", MethodSubstitution.class.getSimpleName()), element, annotation); + } + + if (substitutionMethod.getModifiers().contains(Modifier.ABSTRACT) || substitutionMethod.getModifiers().contains(Modifier.NATIVE)) { + env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must not be native or abstract.", MethodSubstitution.class.getSimpleName()), element, annotation); + } + + String originalName = originalName(substitutionMethod, annotation); + TypeMirror[] originalSignature = originalSignature(substitutionMethod, annotation); + if (originalSignature == null) { + return; + } + ExecutableElement originalMethod = originalMethod(substitutionMethod, annotation, originalType, originalName, originalSignature); + if (DEBUG && originalMethod != null) { + env.getMessager().printMessage(Kind.ERROR, String.format("Found original method %s in type %s.", originalMethod, findEnclosingClass(originalMethod))); + } + } + + private TypeMirror[] originalSignature(ExecutableElement method, AnnotationMirror annotation) { + boolean isStatic = resolveAnnotationValue(Boolean.class, findAnnotationValue(annotation, ORIGINAL_IS_STATIC)); + AnnotationValue signatureValue = findAnnotationValue(annotation, ORIGINAL_SIGNATURE); + String signatureString = resolveAnnotationValue(String.class, signatureValue); + List parameters = new ArrayList<>(); + if (signatureString.equals(ORIGINAL_SIGNATURE_DEFAULT)) { + for (int i = 0; i < method.getParameters().size(); i++) { + parameters.add(method.getParameters().get(i).asType()); + } + if (!isStatic) { + if (parameters.isEmpty()) { + env.getMessager().printMessage(Kind.ERROR, "Method signature must be a static method with the 'this' object as its first parameter", method, annotation); + return null; + } else { + parameters.remove(0); + } + } + parameters.add(0, method.getReturnType()); + } else { + try { + APHotSpotSignature signature = new APHotSpotSignature(signatureString); + parameters.add(signature.getReturnType(env)); + for (int i = 0; i < signature.getParameterCount(false); i++) { + parameters.add(signature.getParameterType(env, i)); + } + } catch (Exception e) { + /* + * That's not good practice and should be changed after APHotSpotSignature has + * received a cleanup. + */ + env.getMessager().printMessage(Kind.ERROR, String.format("Parsing the signature failed: %s", e.getMessage() != null ? e.getMessage() : e.toString()), method, annotation, + signatureValue); + return null; + } + } + return parameters.toArray(new TypeMirror[parameters.size()]); + } + + private static String originalName(ExecutableElement substituteMethod, AnnotationMirror substitution) { + String originalMethodName = resolveAnnotationValue(String.class, findAnnotationValue(substitution, ORIGINAL_METHOD_NAME)); + if (originalMethodName.equals(ORIGINAL_METHOD_NAME_DEFAULT)) { + originalMethodName = substituteMethod.getSimpleName().toString(); + } + return originalMethodName; + } + + private ExecutableElement originalMethod(ExecutableElement substitutionMethod, AnnotationMirror substitutionAnnotation, TypeElement originalType, String originalName, + TypeMirror[] originalSignature) { + TypeMirror signatureReturnType = originalSignature[0]; + TypeMirror[] signatureParameters = Arrays.copyOfRange(originalSignature, 1, originalSignature.length); + List searchElements; + if (originalName.equals("")) { + searchElements = ElementFilter.constructorsIn(originalType.getEnclosedElements()); + } else { + searchElements = ElementFilter.methodsIn(originalType.getEnclosedElements()); + } + + ExecutableElement originalMethod = null; + outer: for (ExecutableElement searchElement : searchElements) { + if (searchElement.getSimpleName().toString().equals(originalName) && searchElement.getParameters().size() == signatureParameters.length) { + for (int i = 0; i < signatureParameters.length; i++) { + VariableElement parameter = searchElement.getParameters().get(i); + if (!isTypeCompatible(parameter.asType(), signatureParameters[i])) { + continue outer; + } + } + originalMethod = searchElement; + break; + } + } + if (originalMethod == null) { + env.getMessager().printMessage(Kind.ERROR, String.format("Could not find the original method with name '%s' and parameters '%s'.", originalName, Arrays.toString(signatureParameters)), + substitutionMethod, substitutionAnnotation); + return null; + } + + if (!isTypeCompatible(originalMethod.getReturnType(), signatureReturnType)) { + env.getMessager().printMessage( + Kind.ERROR, + String.format("The return type of the substitution method '%s' must match with the return type of the original method '%s'.", signatureReturnType, + originalMethod.getReturnType()), substitutionMethod, substitutionAnnotation); + return null; + } + + return originalMethod; + } + + private static boolean isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType) { + /* + * TypeMirrors may contain generic types which deny the types to be equal. So if both types + * are declared we erase the generic types by comparing their corresponding declared + * element. + */ + if (originalType.getKind() == TypeKind.DECLARED && substitutionType.getKind() == TypeKind.DECLARED) { + return ((DeclaredType) originalType).asElement().equals(((DeclaredType) substitutionType).asElement()); + } + return originalType.equals(substitutionType); + } + + private static TypeElement findEnclosingClass(Element element) { + if (element.getKind().isClass()) { + return (TypeElement) element; + } + + Element enclosing = element.getEnclosingElement(); + while (enclosing != null && enclosing.getKind() != ElementKind.PACKAGE) { + if (enclosing.getKind().isClass()) { + return (TypeElement) enclosing; + } + enclosing = enclosing.getEnclosingElement(); + } + return null; + } + +} diff -r 18e2856d1993 -r 467b41309cda graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/VerifierAnnotationProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/VerifierAnnotationProcessor.java Thu Mar 21 18:03:32 2013 +0100 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2013, 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 com.oracle.graal.replacements.verifier; + +import java.lang.annotation.*; +import java.util.*; + +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; + +@SupportedSourceVersion(SourceVersion.RELEASE_7) +public class VerifierAnnotationProcessor extends AbstractProcessor { + + private List verifiers; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!roundEnv.processingOver()) { + for (AbstractVerifier verifier : getVerifiers()) { + Class annotationClass = verifier.getAnnotationClass(); + for (Element e : roundEnv.getElementsAnnotatedWith(annotationClass)) { + AnnotationMirror annotationMirror = findAnnotationMirror(processingEnv, e.getAnnotationMirrors(), annotationClass); + if (annotationMirror == null) { + assert false : "Annotation mirror always expected."; + continue; + } + verifier.verify(e, annotationMirror); + } + } + } + return false; + } + + public static AnnotationMirror findAnnotationMirror(ProcessingEnvironment processingEnv, List mirrors, Class annotationClass) { + TypeElement expectedAnnotationType = processingEnv.getElementUtils().getTypeElement(annotationClass.getCanonicalName()); + for (AnnotationMirror mirror : mirrors) { + DeclaredType annotationType = mirror.getAnnotationType(); + TypeElement actualAnnotationType = (TypeElement) annotationType.asElement(); + if (actualAnnotationType.equals(expectedAnnotationType)) { + return mirror; + } + } + return null; + } + + public List getVerifiers() { + /* Initialized lazily to fail(CNE) when the processor is invoked and not when it is created. */ + if (verifiers == null) { + assert this.processingEnv != null : "ProcessingEnv must be initialized before calling getVerifiers."; + verifiers = new ArrayList<>(); + verifiers.add(new ClassSubstitutionVerifier(this.processingEnv)); + verifiers.add(new MethodSubstitutionVerifier(this.processingEnv)); + } + return verifiers; + } + + @Override + public Set getSupportedAnnotationTypes() { + Set annotationTypes = new HashSet<>(); + for (AbstractVerifier verifier : getVerifiers()) { + annotationTypes.add(verifier.getAnnotationClass().getCanonicalName()); + } + return annotationTypes; + } + +} diff -r 18e2856d1993 -r 467b41309cda mx/projects --- a/mx/projects Thu Mar 21 13:20:32 2013 +0100 +++ b/mx/projects Thu Mar 21 18:03:32 2013 +0100 @@ -195,6 +195,7 @@ project@com.oracle.graal.replacements@dependencies=com.oracle.graal.compiler,com.oracle.graal.java,com.oracle.graal.word project@com.oracle.graal.replacements@checkstyle=com.oracle.graal.graph project@com.oracle.graal.replacements@javaCompliance=1.7 +project@com.oracle.graal.replacements@annotationProcessors=com.oracle.graal.replacements.verifier # graal.replacements.amd64 project@com.oracle.graal.replacements.amd64@subDir=graal @@ -210,6 +211,13 @@ project@com.oracle.graal.replacements.test@checkstyle=com.oracle.graal.graph project@com.oracle.graal.replacements.test@javaCompliance=1.7 +# graal.replacements.verifier +project@com.oracle.graal.replacements.verifier@subDir=graal +project@com.oracle.graal.replacements.verifier@sourceDirs=src +project@com.oracle.graal.replacements.verifier@dependencies=com.oracle.graal.api.replacements +project@com.oracle.graal.replacements.verifier@checkstyle=com.oracle.graal.graph +project@com.oracle.graal.replacements.verifier@javaCompliance=1.7 + # graal.nodes project@com.oracle.graal.nodes@subDir=graal project@com.oracle.graal.nodes@sourceDirs=src