changeset 8428:467b41309cda

Added annotation processor for verifing @ClassSubstitution and @MethodSubstitution annotation at compile time.
author Christian Humer <christian.humer@gmail.com>
date Thu, 21 Mar 2013 18:03:32 +0100
parents 18e2856d1993
children 695abf633f6d
files graal/com.oracle.graal.replacements.verifier/src/META-INF/services/javax.annotation.processing.Processor graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/APHotSpotSignature.java graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/AbstractVerifier.java graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/ClassSubstitutionVerifier.java graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/MethodSubstitutionVerifier.java graal/com.oracle.graal.replacements.verifier/src/com/oracle/graal/replacements/verifier/VerifierAnnotationProcessor.java mx/projects
diffstat 7 files changed, 695 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /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
--- /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<String> 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 + ">";
+    }
+}
--- /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<? extends Annotation> getAnnotationClass();
+
+    @SuppressWarnings("unchecked")
+    protected static <T> T resolveAnnotationValue(Class<T> 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;
+    }
+
+}
--- /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<? extends Annotation> 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;
+    }
+
+}
--- /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<? extends Annotation> 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<TypeMirror> 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<ExecutableElement> searchElements;
+        if (originalName.equals("<init>")) {
+            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;
+    }
+
+}
--- /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<AbstractVerifier> verifiers;
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (!roundEnv.processingOver()) {
+            for (AbstractVerifier verifier : getVerifiers()) {
+                Class<? extends Annotation> 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<? extends AnnotationMirror> 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<AbstractVerifier> 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<String> getSupportedAnnotationTypes() {
+        Set<String> annotationTypes = new HashSet<>();
+        for (AbstractVerifier verifier : getVerifiers()) {
+            annotationTypes.add(verifier.getAnnotationClass().getCanonicalName());
+        }
+        return annotationTypes;
+    }
+
+}
--- 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