diff graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java @ 7502:6343a09b2ec1

Codegen operation generation is inferred from the node type hierarchy.
author Christian Humer <christian.humer@gmail.com>
date Fri, 18 Jan 2013 13:28:12 +0100
parents
children d295ab2902fb
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java	Fri Jan 18 13:28:12 2013 +0100
@@ -0,0 +1,785 @@
+/*
+ * Copyright (c) 2012, 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.truffle.codegen.processor.node;
+
+import java.lang.annotation.*;
+import java.util.*;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+
+import com.oracle.truffle.api.codegen.*;
+import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.api.nodes.Node.Children;
+import com.oracle.truffle.codegen.processor.*;
+import com.oracle.truffle.codegen.processor.ast.*;
+import com.oracle.truffle.codegen.processor.node.NodeFieldData.ExecutionKind;
+import com.oracle.truffle.codegen.processor.node.NodeFieldData.FieldKind;
+import com.oracle.truffle.codegen.processor.template.*;
+import com.oracle.truffle.codegen.processor.typesystem.*;
+
+public class NodeParser extends TemplateParser<NodeData>{
+
+    private static final List<Class<? extends Annotation>> annotations = Arrays.asList(
+                    Generic.class, GuardCheck.class, TypeSystemReference.class, ShortCircuit.class, Specialization.class,
+                    SpecializationGuard.class, SpecializationListener.class, SpecializationThrows.class);
+
+    private static final boolean DEBUG = false;
+
+    private Map<String, NodeData> parsedNodes;
+    private TypeElement originalType;
+
+    public NodeParser(ProcessorContext c) {
+        super(c);
+    }
+
+    @Override
+    protected NodeData parse(Element element, AnnotationMirror mirror) {
+        assert element instanceof TypeElement;
+        try {
+            parsedNodes = new HashMap<>();
+            originalType = (TypeElement) element;
+
+            return parseInnerClassHierarchy((TypeElement) element);
+        } finally {
+            if (DEBUG) {
+                NodeData parsed = parsedNodes.get(Utils.getQualifiedName(originalType));
+                if (parsed != null) {
+                    String dump = parsed.dump();
+                    log.error("Node parsed: %s", dump);
+                    System.out.println("Parsed: " + dump);
+                }
+            }
+            parsedNodes = null;
+            originalType = null;
+        }
+    }
+
+    @Override
+    public boolean isDelegateToRootDeclaredType() {
+        return true;
+    }
+
+    private NodeData parseInnerClassHierarchy(TypeElement rootType) {
+        List<? extends TypeElement> types = ElementFilter.typesIn(rootType.getEnclosedElements());
+        List<NodeData> children = new ArrayList<>();
+        for (TypeElement childElement : types) {
+            NodeData childNode = parseInnerClassHierarchy(childElement);
+            if (childNode != null) {
+                children.add(childNode);
+            }
+        }
+        NodeData rootNode = resolveNode(rootType);
+        if (rootNode == null && children.size() > 0) {
+            rootNode = new NodeData(rootType, null);
+        }
+        if (rootNode != null) {
+            rootNode.setDeclaredChildren(children);
+        }
+        return rootNode;
+    }
+
+    private NodeData resolveNode(TypeElement currentType) {
+        String typeName = Utils.getQualifiedName(currentType);
+        if (!parsedNodes.containsKey(typeName)) {
+            NodeData node = parseNode(currentType);
+            if (node != null) {
+                parsedNodes.put(typeName, node);
+            }
+            return node;
+        }
+        return parsedNodes.get(typeName);
+    }
+
+    private NodeData parseNode(TypeElement type) {
+        if (Utils.findAnnotationMirror(processingEnv, type, GeneratedBy.class) != null) {
+            // generated nodes get called again.
+            return null;
+        }
+        if (!Utils.isAssignable(type.asType(), context.getTruffleTypes().getNode())) {
+            return null; // not a node
+        }
+
+        List<Element> elements = new ArrayList<>(context.getEnvironment().getElementUtils().getAllMembers(type));
+        List<TypeElement> typeHierarchy = findSuperClasses(new ArrayList<TypeElement>(), type);
+        Collections.reverse(typeHierarchy);
+
+        AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class);
+        if (typeSystemMirror == null) {
+            log.error(originalType, "No @%s annotation found in type hierarchy.", TypeSystemReference.class.getSimpleName());
+            return null;
+        }
+
+        TypeMirror typeSytemType = Utils.getAnnotationValueType(typeSystemMirror, "value");
+        final TypeSystemData typeSystem = (TypeSystemData) context.getTemplate(typeSytemType, true);
+        if (typeSystem == null) {
+            log.error(originalType, "The used type system '%s' is invalid.", Utils.getQualifiedName(typeSytemType));
+            return null;
+        }
+
+        NodeData nodeData = new NodeData(type, typeSystem);
+
+        nodeData.setExtensionElements(getExtensionParser().parseAll(type, elements));
+        if (nodeData.getExtensionElements() != null) {
+            elements.addAll(nodeData.getExtensionElements());
+        }
+
+        List<ExecutableTypeData> executableTypes = filterExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements));
+
+        nodeData.setExecutableTypes(executableTypes.toArray(new ExecutableTypeData[executableTypes.size()]));
+
+        parsedNodes.put(Utils.getQualifiedName(type), nodeData); // node fields will resolve node types, to avoid endless loops
+
+        NodeFieldData[] fields = parseFields(nodeData, elements, typeHierarchy);
+        if (fields == null) {
+            return null;
+        }
+        nodeData.setFields(fields);
+
+
+        List<SpecializationData> genericSpecializations = new GenericParser(context, nodeData).parse(elements);
+        List<GuardData> guards = new GuardParser(context, nodeData, nodeData.getTypeSystem()).parse(elements);
+        nodeData.setGuards(guards.toArray(new GuardData[guards.size()]));
+
+        SpecializationMethodParser specializationParser = new SpecializationMethodParser(context, nodeData);
+        List<SpecializationData> specializations = specializationParser.parse(elements);
+        List<ShortCircuitData> shortCircuits = new ShortCircuitParser(context, nodeData).parse(elements);
+        List<TemplateMethod> listeners = new SpecializationListenerParser(context, nodeData).parse(elements);
+
+        if (specializations == null || genericSpecializations == null || shortCircuits == null  || listeners == null || guards == null) {
+            return null;
+        }
+
+        SpecializationData genericSpecialization = null;
+        if (genericSpecializations.size() > 1) {
+            for (SpecializationData generic : genericSpecializations) {
+                log.error(generic.getMethod(), "Only one method with @%s is allowed per operation.", Generic.class.getSimpleName());
+            }
+            return null;
+        } else if (genericSpecializations.size() == 1) {
+            genericSpecialization = genericSpecializations.get(0);
+        }
+
+        if (specializations.size() > 1 && genericSpecialization == null) {
+            log.error(originalType, "Need a @%s method.", Generic.class.getSimpleName());
+            return null;
+        }
+
+        Collections.sort(specializations, new Comparator<SpecializationData>() {
+            @Override
+            public int compare(SpecializationData o1, SpecializationData o2) {
+                return compareSpecialization(typeSystem, o1, o2);
+            }
+        });
+
+        List<SpecializationData> allSpecializations = new ArrayList<>(specializations);
+        if (genericSpecialization != null) {
+            allSpecializations.add(genericSpecialization);
+            CodeExecutableElement uninitializedMethod = new CodeExecutableElement(Utils.modifiers(Modifier.PUBLIC), context.getType(void.class), "doUninitialized");
+            TemplateMethod uninializedMethod = new TemplateMethod(nodeData, genericSpecialization.getSpecification(), uninitializedMethod,
+                            genericSpecialization.getMarkerAnnotation(), genericSpecialization.getReturnType(), genericSpecialization.getParameters());
+            allSpecializations.add(0, new SpecializationData(uninializedMethod, false, true));
+        }
+
+        for (SpecializationData specialization : allSpecializations) {
+            specialization.setNode(nodeData);
+        }
+
+        // verify order is not ambiguous
+        if (!verifySpecializationOrder(typeSystem, specializations)) {
+            return null;
+        }
+
+        nodeData.setSpecializations(allSpecializations.toArray(new SpecializationData[allSpecializations.size()]));
+        nodeData.setSpecializationListeners(listeners.toArray(new TemplateMethod[listeners.size()]));
+
+        if (!verifyMissingAbstractMethods(nodeData, elements)) {
+            return null;
+        }
+
+        if (!assignShortCircuitsToSpecializations(nodeData, allSpecializations, shortCircuits)) {
+            return null;
+        }
+
+        if (!verifyConstructors(nodeData)) {
+            return null;
+        }
+
+        if (!verifyNamingConvention(specializations, "do")) {
+            return null;
+        }
+
+        if (!verifyNamesUnique(specializations)) {
+            return null;
+        }
+
+        if (!verifyNamingConvention(shortCircuits, "needs")) {
+            return null;
+        }
+
+        if (!verifySpecializationThrows(typeSystem, specializations)) {
+            return null;
+        }
+
+        return nodeData;
+    }
+
+    private boolean verifyMissingAbstractMethods(NodeData nodeData, List<Element> elements) {
+        if (nodeData.needsFactory()) {
+            // missing abstract methods only needs to be implemented
+            // if we need go generate factory for it.
+            return true;
+        }
+
+        Set<Element> unusedElements = new HashSet<>(elements);
+        for (TemplateMethod method : nodeData.getAllTemplateMethods()) {
+            unusedElements.remove(method.getMethod());
+        }
+        if (nodeData.getExtensionElements() != null) {
+            unusedElements.removeAll(nodeData.getExtensionElements());
+        }
+
+        boolean valid = true;
+        for (ExecutableElement unusedMethod : ElementFilter.methodsIn(unusedElements)) {
+            if (unusedMethod.getModifiers().contains(Modifier.ABSTRACT)) {
+                context.getLog().error(nodeData.getTemplateType(), "The type %s must implement the inherited abstract method %s.", Utils.getSimpleName(nodeData.getTemplateType()), Utils.getReadableSignature(unusedMethod));
+                valid = false;
+            }
+        }
+
+        return valid;
+    }
+
+    private boolean verifyConstructors(NodeData nodeData) {
+        TypeElement type = Utils.fromTypeMirror(nodeData.getNodeType());
+        if (!nodeData.needsRewrites(context)) {
+            // no specialization constructor is needed if the node never rewrites.
+            return true;
+        }
+
+        List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
+        for (ExecutableElement e : constructors) {
+            if (e.getParameters().size() == 1) {
+                TypeMirror firstArg = e.getParameters().get(0).asType();
+                if (Utils.typeEquals(firstArg, nodeData.getNodeType())) {
+                    if (e.getModifiers().contains(Modifier.PRIVATE)) {
+                        context.getLog().error(e, "The specialization constructor must not be private.");
+                        return false;
+                    } else if (constructors.size() <= 1) {
+                        context.getLog().error(e, "The specialization constructor must not be the only constructor. The definition of an alternative constructor is required.");
+                        return false;
+                    }
+                    return true;
+                }
+            }
+        }
+
+        // not found
+        context.getLog().error(type, "Specialization constructor '%s(%s previousNode) { this(...); }' is required.", Utils.getSimpleName(type), Utils.getSimpleName(type));
+        return false;
+    }
+
+    private static List<ExecutableTypeData> filterExecutableTypes(List<ExecutableTypeData> executableTypes) {
+        List<ExecutableTypeData> filteredExecutableTypes = new ArrayList<>();
+        for (ExecutableTypeData t1 : executableTypes) {
+            boolean add = true;
+            for (ExecutableTypeData t2 : executableTypes) {
+                if (t1 == t2) {
+                    continue;
+                }
+                if (Utils.typeEquals(t1.getType().getPrimitiveType(), t2.getType().getPrimitiveType())) {
+                    if (t1.isFinal() && !t2.isFinal()) {
+                        add = false;
+                    }
+                }
+            }
+            if (add) {
+                filteredExecutableTypes.add(t1);
+            }
+        }
+        return filteredExecutableTypes;
+    }
+
+    private AnnotationMirror findFirstAnnotation(List<? extends Element> elements, Class<? extends Annotation> annotation) {
+        for (Element element : elements) {
+            AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, element, annotation);
+            if (mirror != null) {
+                return mirror;
+            }
+        }
+        return null;
+    }
+
+    private NodeFieldData[] parseFields(NodeData nodeData, List<? extends Element> elements, final List<TypeElement> typeHierarchy) {
+        AnnotationMirror executionOrderMirror = findFirstAnnotation(typeHierarchy, ExecuteChildren.class);
+        List<String> executionDefinition = null;
+        if (executionOrderMirror != null) {
+            executionDefinition = new ArrayList<>();
+            for (Object object : Utils.getAnnotationValueList(executionOrderMirror, "value")) {
+                executionDefinition.add((String) object);
+            }
+        }
+
+        Set<String> shortCircuits = new HashSet<>();
+        for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
+            AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, method, ShortCircuit.class);
+            if (mirror != null) {
+                shortCircuits.add(Utils.getAnnotationValueString(mirror, "value"));
+            }
+        }
+
+        boolean valid = true;
+
+        List<NodeFieldData> fields = new ArrayList<>();
+        for (VariableElement var : ElementFilter.fieldsIn(elements)) {
+            if (var.getModifiers().contains(Modifier.STATIC)) {
+                continue;
+            }
+
+            if (executionDefinition != null) {
+                if (!executionDefinition.contains(var.getSimpleName().toString())) {
+                    continue;
+                }
+            }
+
+            NodeFieldData field = parseField(nodeData, var, shortCircuits);
+            if (field != null) {
+                if (field.getExecutionKind() != ExecutionKind.IGNORE) {
+                    fields.add(field);
+                }
+            } else {
+                valid = false;
+            }
+        }
+
+        //TODO parse getters
+        if (!valid) {
+            return null;
+        }
+
+        NodeFieldData[] fieldArray =  fields.toArray(new NodeFieldData[fields.size()]);
+        sortByExecutionOrder(fieldArray, executionDefinition == null ? Collections.<String>emptyList() : executionDefinition, typeHierarchy);
+        return fieldArray;
+    }
+
+    private NodeFieldData parseField(NodeData parentNodeData, VariableElement var, Set<String> foundShortCircuits) {
+        AnnotationMirror childMirror = Utils.findAnnotationMirror(processingEnv, var, Child.class);
+        AnnotationMirror childrenMirror = Utils.findAnnotationMirror(processingEnv, var, Children.class);
+
+        FieldKind kind;
+
+        ExecutionKind execution;
+        if (foundShortCircuits.contains(var.getSimpleName().toString())) {
+            execution = ExecutionKind.SHORT_CIRCUIT;
+        } else {
+            execution = ExecutionKind.DEFAULT;
+        }
+
+        AnnotationMirror mirror;
+        TypeMirror nodeType;
+
+        if (childMirror != null) {
+            mirror = childMirror;
+            nodeType = var.asType();
+            kind = FieldKind.CHILD;
+        } else if (childrenMirror != null) {
+            mirror = childrenMirror;
+            nodeType = getComponentType(var.asType());
+            kind = FieldKind.CHILDREN;
+        } else {
+            mirror = null;
+            nodeType = null;
+            kind = FieldKind.FIELD;
+            execution = ExecutionKind.IGNORE;
+        }
+
+        NodeData fieldNodeData = null;
+        if (nodeType != null) {
+            fieldNodeData = resolveNode(Utils.fromTypeMirror(nodeType));
+            Element errorElement = Utils.typeEquals(parentNodeData.getTemplateType().asType(), var.getEnclosingElement().asType()) ? var : parentNodeData.getTemplateType();
+
+            if (fieldNodeData == null) {
+                //TODO redirect errors from resolve.
+                context.getLog().error(errorElement, "Node type '%s' is invalid.", Utils.getQualifiedName(nodeType));
+                return null;
+            } else if (fieldNodeData.findGenericExecutableType(context) == null) {
+                context.getLog().error(errorElement, "No executable generic type found for node '%s'.", Utils.getQualifiedName(nodeType));
+                return null;
+            }
+        }
+
+        //TODO correct handling of access elements
+        if (var.getModifiers().contains(Modifier.PRIVATE) && Utils.typeEquals(var.getEnclosingElement().asType(), parentNodeData.getTemplateType().asType())) {
+            execution = ExecutionKind.IGNORE;
+        }
+
+        return new NodeFieldData(fieldNodeData, var, findAccessElement(var), mirror, kind, execution);
+    }
+
+    private Element findAccessElement(VariableElement variableElement) {
+        Element enclosed = variableElement.getEnclosingElement();
+        if (!enclosed.getKind().isClass()) {
+            throw new IllegalArgumentException("Field must be enclosed in a class.");
+        }
+
+        String methodName;
+        if (Utils.typeEquals(variableElement.asType(), context.getType(boolean.class))) {
+            methodName = "is" + Utils.firstLetterUpperCase(variableElement.getSimpleName().toString());
+        } else {
+            methodName = "get" + Utils.firstLetterUpperCase(variableElement.getSimpleName().toString());
+        }
+
+        ExecutableElement getter = null;
+        for (ExecutableElement method : ElementFilter.methodsIn(enclosed.getEnclosedElements())) {
+            if (method.getSimpleName().toString().equals(methodName)
+                            && method.getParameters().size() == 0
+                            && !Utils.typeEquals(method.getReturnType(), context.getType(void.class))) {
+                getter = method;
+                break;
+            }
+        }
+
+        if (getter != null) {
+            return getter;
+        } else {
+            return variableElement;
+        }
+    }
+
+    private static void sortByExecutionOrder(NodeFieldData[] fields, final List<String> executionOrder, final List<TypeElement> typeHierarchy) {
+        Arrays.sort(fields, new Comparator<NodeFieldData>() {
+            @Override
+            public int compare(NodeFieldData o1, NodeFieldData o2) {
+                // sort by execution order
+                int index1 = executionOrder.indexOf(o1.getName());
+                int index2 = executionOrder.indexOf(o2.getName());
+                if (index1 == -1 || index2 == -1) {
+                    // sort by type hierarchy
+                    index1 = typeHierarchy.indexOf(o1.getFieldElement().getEnclosingElement());
+                    index2 = typeHierarchy.indexOf(o2.getFieldElement().getEnclosingElement());
+
+                    // finally sort by name (will emit warning)
+                    if (index1 == -1 || index2 == -1) {
+                        return o1.getName().compareTo(o2.getName());
+                    }
+                }
+                return index1 - index2;
+            }
+        });
+    }
+
+    private boolean assignShortCircuitsToSpecializations(NodeData nodeData,
+                    List<SpecializationData> specializations,
+                    List<ShortCircuitData> shortCircuits) {
+
+        Map<String, List<ShortCircuitData>> groupedShortCircuits = groupShortCircuits(shortCircuits);
+
+        boolean valid = true;
+
+        for (NodeFieldData field : nodeData.filterFields(null, ExecutionKind.SHORT_CIRCUIT)) {
+            String valueName = field.getName();
+            List<ShortCircuitData> availableCircuits = groupedShortCircuits.get(valueName);
+
+            if (availableCircuits == null || availableCircuits.isEmpty()) {
+                log.error(nodeData.getTemplateType(),
+                                "@%s method for short cut value '%s' required.",
+                                ShortCircuit.class.getSimpleName(), valueName);
+                valid = false;
+                continue;
+            }
+
+
+            boolean sameMethodName = true;
+            String methodName = availableCircuits.get(0).getMethodName();
+            for (ShortCircuitData circuit : availableCircuits) {
+                if (!circuit.getMethodName().equals(methodName)) {
+                    sameMethodName = false;
+                }
+            }
+
+            if (!sameMethodName) {
+                for (ShortCircuitData circuit : availableCircuits) {
+                    log.error(circuit.getMethod(), circuit.getMarkerAnnotation(), "All short circuits for short cut value '%s' must have the same method name.", valueName);
+                }
+                valid = false;
+                continue;
+            }
+
+            ShortCircuitData genericCircuit = null;
+            for (ShortCircuitData circuit : availableCircuits) {
+                if (isGenericShortCutMethod(nodeData, circuit)) {
+                    genericCircuit = circuit;
+                    break;
+                }
+            }
+
+            if (genericCircuit == null) {
+                log.error(nodeData.getTemplateType(),
+                                "No generic @%s method available for short cut value '%s'.", ShortCircuit.class.getSimpleName(), valueName);
+                valid = false;
+                continue;
+            }
+
+            for (ShortCircuitData circuit : availableCircuits) {
+                if (circuit != genericCircuit) {
+                    circuit.setGenericShortCircuitMethod(genericCircuit);
+                }
+            }
+        }
+
+        if (!valid) {
+            return valid;
+        }
+
+        NodeFieldData[] fields = nodeData.filterFields(null, ExecutionKind.SHORT_CIRCUIT);
+        for (SpecializationData specialization : specializations) {
+            ShortCircuitData[] assignedShortCuts = new ShortCircuitData[fields.length];
+
+            for (int i = 0; i < fields.length; i++) {
+                List<ShortCircuitData> availableShortCuts = groupedShortCircuits.get(fields[i].getName());
+
+                ShortCircuitData genericShortCircuit = null;
+                for (ShortCircuitData circuit : availableShortCuts) {
+                    if (circuit.isGeneric()) {
+                        genericShortCircuit = circuit;
+                    } else if (circuit.isCompatibleTo(specialization)) {
+                        assignedShortCuts[i] = circuit;
+                    }
+                }
+
+                if (assignedShortCuts[i] == null) {
+                    assignedShortCuts[i] = genericShortCircuit;
+                }
+            }
+            specialization.setShortCircuits(assignedShortCuts);
+        }
+        return true;
+    }
+
+    private boolean verifyNamingConvention(List<? extends TemplateMethod> methods, String prefix) {
+        boolean valid = true;
+        for (int i = 0; i < methods.size(); i++) {
+            TemplateMethod m1 = methods.get(i);
+            if (m1.getMethodName().length() < 3 || !m1.getMethodName().startsWith(prefix)) {
+                log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Naming convention: method name must start with '%s'.", prefix);
+                valid = false;
+            }
+        }
+        return valid;
+    }
+
+    private boolean verifyNamesUnique(List<? extends TemplateMethod> methods) {
+        boolean valid = true;
+        for (int i = 0; i < methods.size(); i++) {
+            TemplateMethod m1 = methods.get(i);
+            for (int j = i + 1; j < methods.size(); j++) {
+                TemplateMethod m2 = methods.get(j);
+
+                if (m1.getMethodName().equalsIgnoreCase(m2.getMethodName())) {
+                    log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Method name '%s' used multiple times", m1.getMethodName());
+                    log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Method name '%s' used multiple times", m1.getMethodName());
+                    return false;
+                }
+            }
+        }
+        return valid;
+    }
+
+
+    private static boolean isGenericShortCutMethod(NodeData node, TemplateMethod method) {
+        for (NodeFieldData field : node.getFields()) {
+            ActualParameter parameter = method.findParameter(field.getName());
+            if (parameter == null) {
+                continue;
+            }
+            if (!Utils.typeEquals(node.getTypeSystem().getGenericType(), parameter.getActualType())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static Map<String, List<ShortCircuitData>> groupShortCircuits(List<ShortCircuitData> shortCircuits) {
+        Map<String, List<ShortCircuitData>> group = new HashMap<>();
+        for (ShortCircuitData shortCircuit : shortCircuits) {
+            List<ShortCircuitData> circuits = group.get(shortCircuit.getValueName());
+            if (circuits == null) {
+                circuits = new ArrayList<>();
+                group.put(shortCircuit.getValueName(), circuits);
+            }
+            circuits.add(shortCircuit);
+        }
+        return group;
+    }
+
+
+    private TypeMirror getComponentType(TypeMirror type) {
+        if (type instanceof ArrayType) {
+            return getComponentType(((ArrayType) type).getComponentType());
+        }
+        return type;
+    }
+
+    private static List<TypeElement> findSuperClasses(List<TypeElement> collection, TypeElement element) {
+        if (element.getSuperclass() != null) {
+            TypeElement superElement = Utils.fromTypeMirror(element.getSuperclass());
+            if (superElement != null) {
+                findSuperClasses(collection, superElement);
+            }
+        }
+        collection.add(element);
+        return collection;
+    }
+
+
+    private boolean verifySpecializationOrder(TypeSystemData typeSystem, List<SpecializationData> specializations) {
+        for (int i = 0; i < specializations.size(); i++) {
+            SpecializationData m1 = specializations.get(i);
+            for (int j = i + 1; j < specializations.size(); j++) {
+                SpecializationData m2 = specializations.get(j);
+                int inferredOrder = compareSpecializationWithoutOrder(typeSystem, m1, m2);
+
+                if (m1.getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
+                    int specOrder = m1.getOrder() - m2.getOrder();
+                    if (specOrder == 0) {
+                        log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Order value %d used multiple times", m1.getOrder());
+                        log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Order value %d used multiple times", m1.getOrder());
+                        return false;
+                    } else if ((specOrder < 0 && inferredOrder > 0) || (specOrder > 0 && inferredOrder < 0)) {
+                        log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
+                        log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
+                        return false;
+                    }
+                } else if (inferredOrder == 0) {
+                    SpecializationData m = (m1.getOrder() == Specialization.DEFAULT_ORDER ? m1 : m2);
+                    log.error(m.getMethod(), m.getMarkerAnnotation(), "Cannot calculate a consistent order for this specialization. Define the order attribute to resolve this.");
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+
+    private boolean verifySpecializationThrows(TypeSystemData typeSystem, List<SpecializationData> specializations) {
+        Map<String, SpecializationData> specializationMap = new HashMap<>();
+        for (SpecializationData spec : specializations) {
+            specializationMap.put(spec.getMethodName(), spec);
+        }
+        boolean valid = true;
+        for (SpecializationData sourceSpecialization : specializations) {
+            if (sourceSpecialization.getExceptions() != null) {
+                for (SpecializationThrowsData throwsData : sourceSpecialization.getExceptions()) {
+                    SpecializationData targetSpecialization = specializationMap.get(throwsData.getTransitionToName());
+                    AnnotationValue value = Utils.getAnnotationValue(throwsData.getAnnotationMirror(), "transitionTo");
+
+                    if (targetSpecialization != null)  {
+                        log.error("Specialization throws current %s target %s compare %s.", sourceSpecialization.getMethodName(), targetSpecialization.getMethodName(), compareSpecialization(typeSystem, sourceSpecialization, targetSpecialization));
+                    }
+
+                    if (targetSpecialization == null) {
+                        log.error(throwsData.getSpecialization().getMethod(), throwsData.getAnnotationMirror(), value,
+                                        "Specialization with name '%s' not found.", throwsData.getTransitionToName());
+                        valid = false;
+                    } else if (compareSpecialization(typeSystem, sourceSpecialization, targetSpecialization) >= 0) {
+                        log.error(throwsData.getSpecialization().getMethod(), throwsData.getAnnotationMirror(), value,
+                                        "The order of the target specializalization must be higher than the source specialization.", throwsData.getTransitionToName());
+                        valid = false;
+                    }
+
+                    for (SpecializationThrowsData otherThrowsData : sourceSpecialization.getExceptions()) {
+                        if (otherThrowsData != throwsData
+                                        && Utils.typeEquals(otherThrowsData.getJavaClass(), throwsData.getJavaClass())) {
+                            AnnotationValue javaClassValue = Utils.getAnnotationValue(throwsData.getAnnotationMirror(), "javaClass");
+                            log.error(throwsData.getSpecialization().getMethod(), throwsData.getAnnotationMirror(), javaClassValue,
+                                            "Duplicate exception type.", throwsData.getTransitionToName());
+                            valid = false;
+                        }
+                    }
+                }
+            }
+        }
+        return valid;
+    }
+
+
+    private static int compareSpecialization(TypeSystemData typeSystem, SpecializationData m1, SpecializationData m2) {
+        if (m1 == m2) {
+            return 0;
+        }
+        int result = compareSpecializationWithoutOrder(typeSystem, m1, m2);
+        if (result == 0) {
+            if (m1.getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
+                return m1.getOrder() - m2.getOrder();
+            }
+        }
+        return result;
+    }
+
+    private static int compareSpecializationWithoutOrder(TypeSystemData typeSystem, SpecializationData m1, SpecializationData m2) {
+        if (m1.getSpecification() != m2.getSpecification()) {
+            throw new UnsupportedOperationException("Cannot compare two specializations with different specifications.");
+        }
+
+        int result = compareActualParameter(typeSystem, m1.getReturnType(), m2.getReturnType());
+
+        for (ParameterSpec spec : m1.getSpecification().getParameters()) {
+            ActualParameter p1 = m1.findParameter(spec);
+            ActualParameter p2 = m2.findParameter(spec);
+
+            if (p1 != null && p2 != null && !Utils.typeEquals(p1.getActualType(), p2.getActualType())) {
+                int typeResult = compareActualParameter(typeSystem, p1, p2);
+                if (result == 0) {
+                    result = typeResult;
+                } else if (Math.signum(result) != Math.signum(typeResult)) {
+                    // We cannot define an order.
+                    return 0;
+                }
+            }
+        }
+        return result;
+    }
+
+    private static int compareActualParameter(TypeSystemData typeSystem, ActualParameter p1, ActualParameter p2) {
+        int index1 = typeSystem.findType(p1.getActualType());
+        int index2 = typeSystem.findType(p2.getActualType());
+
+        assert index1 != index2;
+        assert !(index1 == -1 ^ index2 == -1);
+
+        return index1 - index2;
+    }
+
+
+    @Override
+    public Class< ? extends Annotation> getAnnotationType() {
+        return null;
+    }
+
+    @Override
+    public List<Class< ? extends Annotation>> getTypeDelegatedAnnotationTypes() {
+        return annotations;
+    }
+
+}