diff graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java @ 10597:79041ab43660

Truffle-DSL: API-change: Renamed truffle.api.codegen to truffle.api.dsl for all projects and packages.
author Christian Humer <christian.humer@gmail.com>
date Mon, 01 Jul 2013 20:58:32 +0200
parents
children e93efe3ba5f4
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java	Mon Jul 01 20:58:32 2013 +0200
@@ -0,0 +1,1189 @@
+/*
+ * 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.dsl.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 javax.tools.Diagnostic.Kind;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.dsl.processor.*;
+import com.oracle.truffle.dsl.processor.node.NodeChildData.*;
+import com.oracle.truffle.dsl.processor.template.*;
+import com.oracle.truffle.dsl.processor.template.TemplateMethod.*;
+import com.oracle.truffle.dsl.processor.typesystem.*;
+
+public class NodeParser extends TemplateParser<NodeData> {
+
+    public static final List<Class<? extends Annotation>> ANNOTATIONS = Arrays.asList(Generic.class, TypeSystemReference.class, ShortCircuit.class, Specialization.class, SpecializationListener.class,
+                    NodeContainer.class, NodeChild.class, NodeChildren.class, NodeId.class);
+
+    private Map<String, NodeData> parsedNodes;
+
+    public NodeParser(ProcessorContext c) {
+        super(c);
+    }
+
+    @Override
+    protected NodeData parse(Element element, AnnotationMirror mirror) {
+        assert element instanceof TypeElement;
+        NodeData node = null;
+        try {
+            parsedNodes = new HashMap<>();
+            node = resolveNode((TypeElement) element);
+            if (Log.DEBUG) {
+                NodeData parsed = parsedNodes.get(Utils.getQualifiedName((TypeElement) element));
+                if (node != null) {
+                    String dump = parsed.dump();
+                    log.message(Kind.ERROR, null, null, null, dump);
+                }
+            }
+        } finally {
+            parsedNodes = null;
+        }
+
+        return node;
+    }
+
+    @Override
+    protected NodeData filterErrorElements(NodeData model) {
+        for (Iterator<NodeData> iterator = model.getDeclaredNodes().iterator(); iterator.hasNext();) {
+            NodeData node = filterErrorElements(iterator.next());
+            if (node == null) {
+                iterator.remove();
+            }
+        }
+        if (model.hasErrors()) {
+            return null;
+        }
+        return model;
+    }
+
+    @Override
+    public boolean isDelegateToRootDeclaredType() {
+        return true;
+    }
+
+    @Override
+    public Class<? extends Annotation> getAnnotationType() {
+        return null;
+    }
+
+    @Override
+    public List<Class<? extends Annotation>> getTypeDelegatedAnnotationTypes() {
+        return ANNOTATIONS;
+    }
+
+    private NodeData resolveNode(TypeElement rootType) {
+        String typeName = Utils.getQualifiedName(rootType);
+        if (parsedNodes.containsKey(typeName)) {
+            return parsedNodes.get(typeName);
+        }
+
+        List<? extends TypeElement> types = ElementFilter.typesIn(rootType.getEnclosedElements());
+
+        List<NodeData> children = new ArrayList<>();
+        for (TypeElement childElement : types) {
+            NodeData childNode = resolveNode(childElement);
+            if (childNode != null) {
+                children.add(childNode);
+            }
+        }
+
+        NodeData rootNode = parseNode(rootType);
+        if (rootNode == null && children.size() > 0) {
+            rootNode = new NodeData(rootType, rootType.getSimpleName().toString());
+        }
+
+        parsedNodes.put(typeName, rootNode);
+
+        if (rootNode != null) {
+            children.addAll(rootNode.getDeclaredNodes());
+            rootNode.setDeclaredNodes(children);
+        }
+
+        return rootNode;
+    }
+
+    private NodeData parseNode(TypeElement originalTemplateType) {
+        // reloading the type elements is needed for ecj
+        TypeElement templateType = Utils.fromTypeMirror(context.reloadTypeElement(originalTemplateType));
+
+        if (Utils.findAnnotationMirror(processingEnv, originalTemplateType, GeneratedBy.class) != null) {
+            // generated nodes should not get called again.
+            return null;
+        }
+
+        List<TypeElement> lookupTypes = findSuperClasses(new ArrayList<TypeElement>(), templateType);
+        Collections.reverse(lookupTypes);
+
+        AnnotationMirror nodeClass = findFirstAnnotation(lookupTypes, NodeContainer.class);
+        TypeMirror nodeType = null;
+        if (Utils.isAssignable(context, templateType.asType(), context.getTruffleTypes().getNode())) {
+            nodeType = templateType.asType();
+        }
+        if (nodeClass != null) {
+            nodeType = inheritType(nodeClass, "value", nodeType);
+        }
+
+        if (nodeType == null) {
+            return null;
+        }
+
+        Elements elementUtil = context.getEnvironment().getElementUtils();
+        Set<Element> elementSet = new HashSet<>(elementUtil.getAllMembers(templateType));
+        if (!Utils.typeEquals(templateType.asType(), nodeType)) {
+            elementSet.addAll(elementUtil.getAllMembers(Utils.fromTypeMirror(nodeType)));
+
+            List<TypeElement> nodeLookupTypes = findSuperClasses(new ArrayList<TypeElement>(), Utils.fromTypeMirror(nodeType));
+            Collections.reverse(nodeLookupTypes);
+            lookupTypes.addAll(nodeLookupTypes);
+
+            Set<TypeElement> types = new HashSet<>();
+            for (ListIterator<TypeElement> iterator = lookupTypes.listIterator(); iterator.hasNext();) {
+                TypeElement typeElement = iterator.next();
+                if (types.contains(typeElement)) {
+                    iterator.remove();
+                } else {
+                    types.add(typeElement);
+                }
+            }
+        }
+        List<Element> elements = new ArrayList<>(elementSet);
+
+        NodeData node = parseNodeData(templateType, nodeType, elements, lookupTypes);
+
+        if (node.hasErrors()) {
+            return node; // error sync point
+        }
+
+        parseMethods(node, elements);
+
+        if (node.hasErrors()) {
+            return node;
+        }
+
+        List<NodeData> nodes;
+
+        if (node.isNodeContainer()) {
+            nodes = splitNodeData(node);
+        } else {
+            nodes = new ArrayList<>();
+            nodes.add(node);
+        }
+
+        for (NodeData splittedNode : nodes) {
+            finalizeSpecializations(elements, splittedNode);
+            verifyNode(splittedNode, elements);
+            splittedNode.setPolymorphicSpecializations(createPolymorphicSpecializations(splittedNode));
+            assignShortCircuitsToSpecializations(splittedNode);
+        }
+
+        if (node.isNodeContainer()) {
+            node.setDeclaredNodes(nodes);
+            node.setSpecializationListeners(new ArrayList<SpecializationListenerData>());
+            node.setSpecializations(new ArrayList<SpecializationData>());
+        }
+        return node;
+    }
+
+    private List<SpecializationData> createPolymorphicSpecializations(NodeData node) {
+        if (!node.needsRewrites(context) || node.getPolymorphicDepth() <= 1) {
+            return Collections.emptyList();
+        }
+
+        Signature genericSignature = node.getGenericSpecialization().getSignature();
+        Set<Signature> signatures = new HashSet<>();
+
+        for (SpecializationData specialization1 : node.getSpecializations()) {
+            Signature signature = specialization1.getSignature();
+
+            for (SpecializationData specialization2 : node.getSpecializations()) {
+                if (specialization1 == specialization2) {
+                    continue;
+                }
+                signatures.add(signature.combine(genericSignature, specialization2.getSignature()));
+            }
+        }
+
+        while (true) {
+            List<Signature> newSignatures = new ArrayList<>();
+            for (Signature signature1 : signatures) {
+                for (Signature signature2 : signatures) {
+                    if (signature1 == signature2) {
+                        continue;
+                    }
+                    newSignatures.add(signature1.combine(genericSignature, signature2));
+                }
+            }
+            if (!signatures.addAll(newSignatures)) {
+                break;
+            }
+        }
+
+        List<Signature> sortedSignatures = new ArrayList<>(signatures);
+        Collections.sort(sortedSignatures);
+
+        List<SpecializationData> specializations = new ArrayList<>();
+        SpecializationData generic = node.getGenericSpecialization();
+        for (Signature signature : sortedSignatures) {
+            SpecializationData specialization = new SpecializationData(generic, false, false, true);
+            specialization.forceFrame(context.getTruffleTypes().getFrame());
+            specialization.setNode(node);
+            specialization.updateSignature(signature);
+
+            if (specialization.isGenericSpecialization(context)) {
+                specializations.add(0, specialization);
+            } else {
+                specializations.add(specialization);
+            }
+        }
+
+        return specializations;
+    }
+
+    private NodeData parseNodeData(TypeElement templateType, TypeMirror nodeType, List<? extends Element> elements, List<TypeElement> lookupTypes) {
+        NodeData nodeData = new NodeData(templateType, templateType.getSimpleName().toString());
+
+        AnnotationMirror typeSystemMirror = findFirstAnnotation(lookupTypes, TypeSystemReference.class);
+        if (typeSystemMirror == null) {
+            nodeData.addError("No @%s annotation found in type hierarchy of %s.", TypeSystemReference.class.getSimpleName(), Utils.getQualifiedName(nodeType));
+            return nodeData;
+        }
+
+        TypeMirror typeSytemType = Utils.getAnnotationValue(TypeMirror.class, typeSystemMirror, "value");
+        final TypeSystemData typeSystem = (TypeSystemData) context.getTemplate(typeSytemType, true);
+        if (typeSystem == null) {
+            nodeData.addError("The used type system '%s' is invalid or not a Node.", Utils.getQualifiedName(typeSytemType));
+            return nodeData;
+        }
+
+        AnnotationMirror polymorphicMirror = findFirstAnnotation(lookupTypes, PolymorphicLimit.class);
+        if (polymorphicMirror != null) {
+            AnnotationValue limitValue = Utils.getAnnotationValue(polymorphicMirror, "value");
+            int polymorphicLimit = Utils.getAnnotationValue(Integer.class, polymorphicMirror, "value");
+            if (polymorphicLimit < 1) {
+                nodeData.addError(limitValue, "Invalid polymorphic limit %s.", polymorphicLimit);
+            }
+            nodeData.setPolymorphicDepth(polymorphicLimit);
+        }
+
+        List<String> assumptionsList = new ArrayList<>();
+        for (int i = lookupTypes.size() - 1; i >= 0; i--) {
+            TypeElement type = lookupTypes.get(i);
+            AnnotationMirror assumptions = Utils.findAnnotationMirror(context.getEnvironment(), type, NodeAssumptions.class);
+            if (assumptions != null) {
+                List<String> assumptionStrings = Utils.getAnnotationValueList(String.class, assumptions, "value");
+                for (String string : assumptionStrings) {
+                    if (assumptionsList.contains(string)) {
+                        assumptionsList.remove(string);
+                    }
+                    assumptionsList.add(string);
+                }
+            }
+        }
+        AnnotationMirror nodeInfoMirror = findFirstAnnotation(lookupTypes, NodeInfo.class);
+        if (nodeInfoMirror != null) {
+            nodeData.setShortName(Utils.getAnnotationValue(String.class, nodeInfoMirror, "shortName"));
+        }
+
+        nodeData.setAssumptions(new ArrayList<>(assumptionsList));
+        nodeData.setNodeType(nodeType);
+        AnnotationMirror nodeContainer = findFirstAnnotation(lookupTypes, NodeContainer.class);
+        nodeData.setNodeContainer(nodeContainer != null);
+        nodeData.setTypeSystem(typeSystem);
+        nodeData.setFields(parseFields(elements));
+        parsedNodes.put(Utils.getQualifiedName(templateType), nodeData);
+        // parseChildren invokes cyclic parsing.
+        nodeData.setChildren(parseChildren(elements, lookupTypes));
+        nodeData.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements)));
+
+        return nodeData;
+    }
+
+    private static List<NodeFieldData> parseFields(List<? extends Element> elements) {
+        List<NodeFieldData> fields = new ArrayList<>();
+        for (VariableElement field : ElementFilter.fieldsIn(elements)) {
+            if (field.getModifiers().contains(Modifier.STATIC)) {
+                continue;
+            }
+            if (field.getModifiers().contains(Modifier.PUBLIC) || field.getModifiers().contains(Modifier.PROTECTED)) {
+                fields.add(new NodeFieldData(field));
+            }
+        }
+        return fields;
+    }
+
+    private List<NodeChildData> parseChildren(List<? extends Element> elements, final List<TypeElement> typeHierarchy) {
+        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.getAnnotationValue(String.class, mirror, "value"));
+            }
+        }
+        Map<String, TypeMirror> castNodeTypes = new HashMap<>();
+        for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
+            AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, method, CreateCast.class);
+            if (mirror != null) {
+                List<String> children = (Utils.getAnnotationValueList(String.class, mirror, "value"));
+                if (children != null) {
+                    for (String child : children) {
+                        castNodeTypes.put(child, method.getReturnType());
+                    }
+                }
+            }
+        }
+
+        List<NodeChildData> parsedChildren = new ArrayList<>();
+        List<TypeElement> typeHierarchyReversed = new ArrayList<>(typeHierarchy);
+        Collections.reverse(typeHierarchyReversed);
+        for (TypeElement type : typeHierarchyReversed) {
+            AnnotationMirror nodeClassMirror = Utils.findAnnotationMirror(processingEnv, type, NodeContainer.class);
+            AnnotationMirror nodeChildrenMirror = Utils.findAnnotationMirror(processingEnv, type, NodeChildren.class);
+
+            TypeMirror nodeClassType = type.getSuperclass();
+            if (!Utils.isAssignable(context, nodeClassType, context.getTruffleTypes().getNode())) {
+                nodeClassType = null;
+            }
+
+            if (nodeClassMirror != null) {
+                nodeClassType = inheritType(nodeClassMirror, "value", nodeClassType);
+            }
+
+            List<AnnotationMirror> children = Utils.collectAnnotations(context, nodeChildrenMirror, "value", type, NodeChild.class);
+            int index = 0;
+            for (AnnotationMirror childMirror : children) {
+                String name = Utils.getAnnotationValue(String.class, childMirror, "value");
+                if (name.equals("")) {
+                    name = "child" + index;
+                }
+
+                Cardinality cardinality = Cardinality.ONE;
+
+                TypeMirror childType = inheritType(childMirror, "type", nodeClassType);
+                if (childType.getKind() == TypeKind.ARRAY) {
+                    cardinality = Cardinality.MANY;
+                }
+
+                TypeMirror originalChildType = childType;
+                TypeMirror castNodeType = castNodeTypes.get(name);
+                if (castNodeType != null) {
+                    childType = castNodeType;
+                }
+
+                Element getter = findGetter(elements, name, childType);
+
+                ExecutionKind kind = ExecutionKind.DEFAULT;
+                if (shortCircuits.contains(name)) {
+                    kind = ExecutionKind.SHORT_CIRCUIT;
+                }
+
+                NodeChildData nodeChild = new NodeChildData(type, childMirror, name, childType, originalChildType, getter, cardinality, kind);
+
+                parsedChildren.add(nodeChild);
+
+                verifyNodeChild(nodeChild);
+                if (nodeChild.hasErrors()) {
+                    continue;
+                }
+
+                NodeData fieldNodeData = resolveNode(Utils.fromTypeMirror(childType));
+                nodeChild.setNode(fieldNodeData);
+                if (fieldNodeData == null) {
+                    nodeChild.addError("Node type '%s' is invalid or not a valid Node.", Utils.getQualifiedName(childType));
+                }
+
+            }
+            index++;
+        }
+
+        List<NodeChildData> filteredChildren = new ArrayList<>();
+        Set<String> encounteredNames = new HashSet<>();
+        for (int i = parsedChildren.size() - 1; i >= 0; i--) {
+            NodeChildData child = parsedChildren.get(i);
+            if (!encounteredNames.contains(child.getName())) {
+                filteredChildren.add(0, child);
+                encounteredNames.add(child.getName());
+            }
+        }
+
+        for (NodeChildData child : filteredChildren) {
+            List<String> executeWithStrings = Utils.getAnnotationValueList(String.class, child.getMessageAnnotation(), "executeWith");
+            AnnotationValue executeWithValue = Utils.getAnnotationValue(child.getMessageAnnotation(), "executeWith");
+            List<NodeChildData> executeWith = new ArrayList<>();
+            for (String executeWithString : executeWithStrings) {
+
+                if (child.getName().equals(executeWithString)) {
+                    child.addError(executeWithValue, "The child node '%s' cannot be executed with itself.", executeWithString);
+                    continue;
+                }
+
+                NodeChildData found = null;
+                boolean before = true;
+                for (NodeChildData resolveChild : filteredChildren) {
+                    if (resolveChild == child) {
+                        before = false;
+                        continue;
+                    }
+                    if (resolveChild.getName().equals(executeWithString)) {
+                        found = resolveChild;
+                        break;
+                    }
+                }
+
+                if (found == null) {
+                    child.addError(executeWithValue, "The child node '%s' cannot be executed with '%s'. The child node was not found.", child.getName(), executeWithString);
+                    continue;
+                } else if (!before) {
+                    child.addError(executeWithValue, "The child node '%s' cannot be executed with '%s'. The node %s is executed after the current node.", child.getName(), executeWithString,
+                                    executeWithString);
+                    continue;
+                }
+                executeWith.add(found);
+            }
+            child.setExecuteWith(executeWith);
+            if (child.getNodeData() == null) {
+                continue;
+            }
+
+            List<ExecutableTypeData> types = child.findGenericExecutableTypes(context);
+            if (types.isEmpty()) {
+                child.addError(executeWithValue, "No generic execute method found with %s evaluated arguments for node type %s.", executeWith.size(), Utils.getSimpleName(child.getNodeType()));
+                continue;
+            }
+        }
+
+        return filteredChildren;
+    }
+
+    private void parseMethods(final NodeData node, List<Element> elements) {
+        node.setShortCircuits(new ShortCircuitParser(context, node).parse(elements));
+        node.setSpecializationListeners(new SpecializationListenerParser(context, node).parse(elements));
+        List<SpecializationData> generics = new GenericParser(context, node).parse(elements);
+        List<SpecializationData> specializations = new SpecializationMethodParser(context, node).parse(elements);
+        node.setCasts(new CreateCastParser(context, node).parse(elements));
+
+        List<SpecializationData> allSpecializations = new ArrayList<>();
+        allSpecializations.addAll(generics);
+        allSpecializations.addAll(specializations);
+
+        node.setSpecializations(allSpecializations);
+    }
+
+    private static List<NodeData> splitNodeData(NodeData node) {
+        SortedMap<String, List<SpecializationData>> groupedSpecializations = groupByNodeId(node.getSpecializations());
+        SortedMap<String, List<SpecializationListenerData>> groupedListeners = groupByNodeId(node.getSpecializationListeners());
+        SortedMap<String, List<CreateCastData>> groupedCasts = groupByNodeId(node.getCasts());
+
+        Set<String> ids = new TreeSet<>();
+        ids.addAll(groupedSpecializations.keySet());
+        ids.addAll(groupedListeners.keySet());
+
+        List<NodeData> splitted = new ArrayList<>();
+        for (String id : ids) {
+            List<SpecializationData> specializations = groupedSpecializations.get(id);
+            List<SpecializationListenerData> listeners = groupedListeners.get(id);
+            List<CreateCastData> casts = groupedCasts.get(id);
+
+            if (specializations == null) {
+                specializations = new ArrayList<>();
+            }
+
+            if (listeners == null) {
+                listeners = new ArrayList<>();
+            }
+
+            String nodeId = node.getNodeId();
+            if (nodeId.endsWith("Node") && !nodeId.equals("Node")) {
+                nodeId = nodeId.substring(0, nodeId.length() - 4);
+            }
+            String newNodeId = nodeId + Utils.firstLetterUpperCase(id);
+            NodeData copy = new NodeData(node, id, newNodeId);
+
+            copy.setSpecializations(specializations);
+            copy.setSpecializationListeners(listeners);
+            copy.setCasts(casts);
+
+            splitted.add(copy);
+        }
+
+        node.setSpecializations(new ArrayList<SpecializationData>());
+        node.setSpecializationListeners(new ArrayList<SpecializationListenerData>());
+        node.setCasts(new ArrayList<CreateCastData>());
+
+        return splitted;
+    }
+
+    private void finalizeSpecializations(List<Element> elements, final NodeData node) {
+        List<SpecializationData> specializations = new ArrayList<>(node.getSpecializations());
+
+        if (specializations.isEmpty()) {
+            return;
+        }
+
+        for (SpecializationData specialization : specializations) {
+            matchGuards(elements, specialization);
+        }
+
+        List<SpecializationData> generics = new ArrayList<>();
+        for (SpecializationData spec : specializations) {
+            if (spec.isGeneric()) {
+                generics.add(spec);
+            }
+        }
+
+        if (generics.size() == 1 && specializations.size() == 1) {
+            for (SpecializationData generic : generics) {
+                generic.addError("@%s defined but no @%s.", Generic.class.getSimpleName(), Specialization.class.getSimpleName());
+            }
+        }
+
+        SpecializationData genericSpecialization = null;
+        if (generics.size() > 1) {
+            for (SpecializationData generic : generics) {
+                generic.addError("Only @%s is allowed per operation.", Generic.class.getSimpleName());
+            }
+            return;
+        } else if (generics.size() == 1) {
+            genericSpecialization = generics.get(0);
+        } else if (node.needsRewrites(context)) {
+            SpecializationData specialization = specializations.get(0);
+            GenericParser parser = new GenericParser(context, node);
+            MethodSpec specification = parser.createDefaultMethodSpec(specialization.getMethod(), null, true, null);
+
+            ExecutableTypeData anyGenericReturnType = node.findAnyGenericExecutableType(context, 0);
+            assert anyGenericReturnType != null;
+
+            ActualParameter returnType = new ActualParameter(specification.getReturnType(), anyGenericReturnType.getType(), 0, false);
+            List<ActualParameter> parameters = new ArrayList<>();
+            for (ActualParameter specializationParameter : specialization.getParameters()) {
+                ParameterSpec parameterSpec = specification.findParameterSpec(specializationParameter.getSpecification().getName());
+                NodeChildData child = node.findChild(parameterSpec.getName());
+                TypeData actualType;
+                if (child == null) {
+                    actualType = specializationParameter.getTypeSystemType();
+                } else {
+                    ExecutableTypeData paramType = child.findAnyGenericExecutableType(context);
+                    assert paramType != null;
+                    actualType = paramType.getType();
+                }
+
+                if (actualType != null) {
+                    parameters.add(new ActualParameter(parameterSpec, actualType, specializationParameter.getIndex(), specializationParameter.isImplicit()));
+                } else {
+                    parameters.add(new ActualParameter(parameterSpec, specializationParameter.getType(), specializationParameter.getIndex(), specializationParameter.isImplicit()));
+                }
+            }
+            TemplateMethod genericMethod = new TemplateMethod("Generic", node, specification, null, null, returnType, parameters);
+            genericSpecialization = new SpecializationData(genericMethod, true, false, false);
+
+            specializations.add(genericSpecialization);
+        }
+
+        if (genericSpecialization != null) {
+            for (ActualParameter parameter : genericSpecialization.getReturnTypeAndParameters()) {
+                if (Utils.isObject(parameter.getType())) {
+                    continue;
+                }
+                Set<String> types = new HashSet<>();
+                for (SpecializationData specialization : specializations) {
+                    ActualParameter actualParameter = specialization.findParameter(parameter.getLocalName());
+                    if (actualParameter != null) {
+                        types.add(Utils.getQualifiedName(actualParameter.getType()));
+                    }
+                }
+                if (types.size() > 1) {
+                    genericSpecialization.replaceParameter(parameter.getLocalName(), new ActualParameter(parameter, node.getTypeSystem().getGenericTypeData()));
+                }
+            }
+            TemplateMethod uninializedMethod = new TemplateMethod("Uninitialized", node, genericSpecialization.getSpecification(), null, null, genericSpecialization.getReturnType(),
+                            genericSpecialization.getParameters());
+            // should not use messages from generic specialization
+            uninializedMethod.getMessages().clear();
+            specializations.add(new SpecializationData(uninializedMethod, false, true, false));
+        }
+
+        Collections.sort(specializations);
+
+        node.setSpecializations(specializations);
+
+        List<SpecializationData> needsId = new ArrayList<>();
+        for (SpecializationData specialization : specializations) {
+            if (specialization.isGeneric()) {
+                specialization.setId("Generic");
+            } else if (specialization.isUninitialized()) {
+                specialization.setId("Uninitialized");
+            } else {
+                needsId.add(specialization);
+            }
+        }
+
+        // verify specialization parameter length
+        if (verifySpecializationParameters(node)) {
+            List<String> ids = calculateSpecializationIds(needsId);
+            for (int i = 0; i < ids.size(); i++) {
+                needsId.get(i).setId(ids.get(i));
+            }
+        }
+
+        // calculate reachability
+        int specializationCount = 0;
+        boolean reachable = true;
+        for (SpecializationData specialization : specializations) {
+            if (specialization.isUninitialized()) {
+                specialization.setReachable(true);
+                continue;
+            }
+            if (!reachable && specialization.getMethod() != null) {
+                specialization.addError("%s is not reachable.", specialization.isGeneric() ? "Generic" : "Specialization");
+            }
+            specialization.setReachable(reachable);
+            if (!specialization.hasRewrite(context)) {
+                reachable = false;
+            }
+            if (!specialization.isGeneric()) {
+                specializationCount++;
+            }
+        }
+
+        if (node.getPolymorphicDepth() < 0) {
+            node.setPolymorphicDepth(specializationCount - 1);
+        }
+
+        // reduce polymorphicness if generic is not reachable
+        if (node.getGenericSpecialization() != null && !node.getGenericSpecialization().isReachable()) {
+            node.setPolymorphicDepth(1);
+        }
+    }
+
+    private void assignShortCircuitsToSpecializations(NodeData node) {
+        Map<String, List<ShortCircuitData>> groupedShortCircuits = groupShortCircuits(node.getShortCircuits());
+
+        boolean valid = true;
+        for (NodeChildData field : node.filterFields(ExecutionKind.SHORT_CIRCUIT)) {
+            String valueName = field.getName();
+            List<ShortCircuitData> availableCircuits = groupedShortCircuits.get(valueName);
+
+            if (availableCircuits == null || availableCircuits.isEmpty()) {
+                node.addError("@%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) {
+                    circuit.addError("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(node, circuit)) {
+                    genericCircuit = circuit;
+                    break;
+                }
+            }
+
+            if (genericCircuit == null) {
+                node.addError("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;
+        }
+
+        NodeChildData[] fields = node.filterFields(ExecutionKind.SHORT_CIRCUIT);
+        List<SpecializationData> specializations = new ArrayList<>();
+        specializations.addAll(node.getSpecializations());
+        specializations.addAll(node.getPolymorphicSpecializations());
+
+        for (SpecializationData specialization : specializations) {
+            List<ShortCircuitData> assignedShortCuts = new ArrayList<>(fields.length);
+
+            for (int i = 0; i < fields.length; i++) {
+                List<ShortCircuitData> availableShortCuts = groupedShortCircuits.get(fields[i].getName());
+
+                ShortCircuitData genericShortCircuit = null;
+                ShortCircuitData compatibleShortCircuit = null;
+                for (ShortCircuitData circuit : availableShortCuts) {
+                    if (circuit.isGeneric()) {
+                        genericShortCircuit = circuit;
+                    } else if (circuit.isCompatibleTo(specialization)) {
+                        compatibleShortCircuit = circuit;
+                    }
+                }
+
+                if (compatibleShortCircuit == null) {
+                    compatibleShortCircuit = genericShortCircuit;
+                }
+                assignedShortCuts.add(compatibleShortCircuit);
+            }
+            specialization.setShortCircuits(assignedShortCuts);
+        }
+    }
+
+    private void matchGuards(List<Element> elements, SpecializationData specialization) {
+        if (specialization.getGuardDefinitions().isEmpty()) {
+            specialization.setGuards(Collections.<GuardData> emptyList());
+            return;
+        }
+
+        List<GuardData> foundGuards = new ArrayList<>();
+        List<ExecutableElement> methods = ElementFilter.methodsIn(elements);
+        for (String guardDefinition : specialization.getGuardDefinitions()) {
+            GuardParser parser = new GuardParser(context, specialization, guardDefinition);
+            List<GuardData> guards = parser.parse(methods);
+            if (!guards.isEmpty()) {
+                foundGuards.add(guards.get(0));
+            } else {
+                // error no guard found
+                MethodSpec spec = parser.createSpecification(specialization.getMethod(), null);
+                spec.applyTypeDefinitions("types");
+                specialization.addError("Guard with method name '%s' not found. Expected signature: %n%s", guardDefinition, spec.toSignatureString("guard"));
+            }
+        }
+
+        specialization.setGuards(foundGuards);
+
+    }
+
+    private static List<String> calculateSpecializationIds(List<SpecializationData> specializations) {
+        int lastSize = -1;
+        List<List<String>> signatureChunks = new ArrayList<>();
+        for (SpecializationData other : specializations) {
+            if (other.isUninitialized() || other.isGeneric()) {
+                continue;
+            }
+            List<String> paramIds = new LinkedList<>();
+            paramIds.add(Utils.getTypeId(other.getReturnType().getType()));
+            for (ActualParameter param : other.getParameters()) {
+                if (other.getNode().findChild(param.getSpecification().getName()) == null) {
+                    continue;
+                }
+                paramIds.add(Utils.getTypeId(param.getType()));
+            }
+            assert lastSize == -1 || lastSize == paramIds.size();
+            if (lastSize != -1 && lastSize != paramIds.size()) {
+                throw new AssertionError();
+            }
+            signatureChunks.add(paramIds);
+            lastSize = paramIds.size();
+        }
+
+        // reduce id vertically
+        for (int i = 0; i < lastSize; i++) {
+            String prev = null;
+            boolean allSame = true;
+            for (List<String> signature : signatureChunks) {
+                String arg = signature.get(i);
+                if (prev == null) {
+                    prev = arg;
+                    continue;
+                } else if (!prev.equals(arg)) {
+                    allSame = false;
+                    break;
+                }
+                prev = arg;
+            }
+
+            if (allSame) {
+                for (List<String> signature : signatureChunks) {
+                    signature.remove(i);
+                }
+                lastSize--;
+            }
+        }
+
+        // reduce id horizontally
+        for (List<String> signature : signatureChunks) {
+            if (signature.isEmpty()) {
+                continue;
+            }
+            String prev = null;
+            boolean allSame = true;
+            for (String arg : signature) {
+                if (prev == null) {
+                    prev = arg;
+                    continue;
+                } else if (!prev.equals(arg)) {
+                    allSame = false;
+                    break;
+                }
+                prev = arg;
+            }
+
+            if (allSame) {
+                signature.clear();
+                signature.add(prev);
+            }
+        }
+
+        // create signatures
+        List<String> signatures = new ArrayList<>();
+        for (List<String> signatureChunk : signatureChunks) {
+            StringBuilder b = new StringBuilder();
+            if (signatureChunk.isEmpty()) {
+                b.append("Default");
+            } else {
+                for (String s : signatureChunk) {
+                    b.append(s);
+                }
+            }
+            signatures.add(b.toString());
+        }
+
+        Map<String, Integer> counts = new HashMap<>();
+        for (String s1 : signatures) {
+            Integer count = counts.get(s1);
+            if (count == null) {
+                count = 0;
+            }
+            count++;
+            counts.put(s1, count);
+        }
+
+        for (String s : counts.keySet()) {
+            int count = counts.get(s);
+            if (count > 1) {
+                int number = 0;
+                for (ListIterator<String> iterator = signatures.listIterator(); iterator.hasNext();) {
+                    String s2 = iterator.next();
+                    if (s.equals(s2)) {
+                        iterator.set(s2 + number);
+                        number++;
+                    }
+                }
+            }
+        }
+
+        return signatures;
+    }
+
+    private void verifyNode(NodeData nodeData, List<? extends Element> elements) {
+        // verify order is not ambiguous
+        verifySpecializationOrder(nodeData);
+
+        verifyMissingAbstractMethods(nodeData, elements);
+
+        verifyConstructors(nodeData);
+
+        verifyNamingConvention(nodeData.getShortCircuits(), "needs");
+
+        verifySpecializationThrows(nodeData);
+    }
+
+    private static void verifyNodeChild(NodeChildData nodeChild) {
+        if (nodeChild.getNodeType() == null) {
+            nodeChild.addError("No valid node type could be resoleved.");
+        }
+        // FIXME verify node child
+        // FIXME verify node type set
+    }
+
+    private static void verifyMissingAbstractMethods(NodeData nodeData, List<? extends Element> originalElements) {
+        if (!nodeData.needsFactory()) {
+            // missing abstract methods only needs to be implemented
+            // if we need go generate factory for it.
+            return;
+        }
+
+        List<Element> elements = new ArrayList<>(originalElements);
+
+        Set<Element> unusedElements = new HashSet<>(elements);
+        for (TemplateMethod method : nodeData.getAllTemplateMethods()) {
+            unusedElements.remove(method.getMethod());
+        }
+        if (nodeData.getExtensionElements() != null) {
+            unusedElements.removeAll(nodeData.getExtensionElements());
+        }
+
+        for (NodeChildData child : nodeData.getChildren()) {
+            if (child.getAccessElement() != null) {
+                unusedElements.remove(child.getAccessElement());
+            }
+        }
+
+        for (ExecutableElement unusedMethod : ElementFilter.methodsIn(unusedElements)) {
+            if (unusedMethod.getModifiers().contains(Modifier.ABSTRACT)) {
+                nodeData.addError("The type %s must implement the inherited abstract method %s.", Utils.getSimpleName(nodeData.getTemplateType()), Utils.getReadableSignature(unusedMethod));
+            }
+        }
+    }
+
+    private void verifyConstructors(NodeData nodeData) {
+        if (!nodeData.needsRewrites(context)) {
+            // no specialization constructor is needed if the node never rewrites.
+            return;
+        }
+
+        TypeElement type = Utils.fromTypeMirror(nodeData.getNodeType());
+        List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
+
+        boolean parametersFound = false;
+        for (ExecutableElement constructor : constructors) {
+            if (!constructor.getParameters().isEmpty()) {
+                parametersFound = true;
+            }
+        }
+        if (!parametersFound) {
+            return;
+        }
+        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)) {
+                        nodeData.addError("The specialization constructor must not be private.");
+                    } else if (constructors.size() <= 1) {
+                        nodeData.addError("The specialization constructor must not be the only constructor. The definition of an alternative constructor is required.");
+                    }
+                    return;
+                }
+            }
+        }
+
+        // not found
+        nodeData.addError("Specialization constructor '%s(%s previousNode) { this(...); }' is required.", Utils.getSimpleName(type), Utils.getSimpleName(type));
+    }
+
+    private static boolean verifySpecializationParameters(NodeData nodeData) {
+        boolean valid = true;
+        int args = -1;
+        for (SpecializationData specializationData : nodeData.getSpecializations()) {
+            int signatureArgs = 0;
+            for (ActualParameter param : specializationData.getParameters()) {
+                if (param.getSpecification().isSignature()) {
+                    signatureArgs++;
+                }
+            }
+            if (args != -1 && args != signatureArgs) {
+                valid = false;
+                break;
+            }
+            args = signatureArgs;
+        }
+        if (!valid) {
+            for (SpecializationData specialization : nodeData.getSpecializations()) {
+                specialization.addError("All specializations must have the same number of arguments.");
+            }
+        }
+        return valid;
+    }
+
+    private static void verifySpecializationOrder(NodeData node) {
+        List<SpecializationData> specializations = node.getSpecializations();
+        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 = m1.compareBySignature(m2);
+
+                if (m1.getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
+                    int specOrder = m1.getOrder() - m2.getOrder();
+                    if (specOrder == 0) {
+                        m1.addError("Order value %d used multiple times", m1.getOrder());
+                        m2.addError("Order value %d used multiple times", m1.getOrder());
+                        return;
+                    } else if ((specOrder < 0 && inferredOrder > 0) || (specOrder > 0 && inferredOrder < 0)) {
+                        m1.addError("Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
+                        m2.addError("Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
+                        return;
+                    }
+                } else if (inferredOrder == 0) {
+                    SpecializationData m = (m1.getOrder() == Specialization.DEFAULT_ORDER ? m1 : m2);
+                    m.addError("Cannot calculate a consistent order for this specialization. Define the order attribute to resolve this.");
+                    return;
+                }
+            }
+        }
+    }
+
+    private static void verifySpecializationThrows(NodeData node) {
+        Map<String, SpecializationData> specializationMap = new HashMap<>();
+        for (SpecializationData spec : node.getSpecializations()) {
+            specializationMap.put(spec.getMethodName(), spec);
+        }
+        for (SpecializationData sourceSpecialization : node.getSpecializations()) {
+            if (sourceSpecialization.getExceptions() != null) {
+                for (SpecializationThrowsData throwsData : sourceSpecialization.getExceptions()) {
+                    for (SpecializationThrowsData otherThrowsData : sourceSpecialization.getExceptions()) {
+                        if (otherThrowsData != throwsData && Utils.typeEquals(otherThrowsData.getJavaClass(), throwsData.getJavaClass())) {
+                            throwsData.addError("Duplicate exception type.");
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private static void verifyNamingConvention(List<? extends TemplateMethod> methods, String prefix) {
+        for (int i = 0; i < methods.size(); i++) {
+            TemplateMethod m1 = methods.get(i);
+            if (m1.getMethodName().length() < 3 || !m1.getMethodName().startsWith(prefix)) {
+                m1.addError("Naming convention: method name must start with '%s'.", prefix);
+            }
+        }
+    }
+
+    private static Map<Integer, List<ExecutableTypeData>> groupExecutableTypes(List<ExecutableTypeData> executableTypes) {
+        Map<Integer, List<ExecutableTypeData>> groupedTypes = new HashMap<>();
+        for (ExecutableTypeData type : executableTypes) {
+            int evaluatedCount = type.getEvaluatedCount();
+
+            List<ExecutableTypeData> types = groupedTypes.get(evaluatedCount);
+            if (types == null) {
+                types = new ArrayList<>();
+                groupedTypes.put(evaluatedCount, types);
+            }
+            types.add(type);
+        }
+
+        for (List<ExecutableTypeData> types : groupedTypes.values()) {
+            Collections.sort(types);
+        }
+        return groupedTypes;
+    }
+
+    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 TypeMirror inheritType(AnnotationMirror annotation, String valueName, TypeMirror parentType) {
+        TypeMirror inhertNodeType = context.getTruffleTypes().getNode();
+        TypeMirror value = Utils.getAnnotationValue(TypeMirror.class, annotation, valueName);
+        if (Utils.typeEquals(inhertNodeType, value)) {
+            return parentType;
+        } else {
+            return value;
+        }
+    }
+
+    private Element findGetter(List<? extends Element> elements, String variableName, TypeMirror type) {
+        if (type == null) {
+            return null;
+        }
+        String methodName;
+        if (Utils.typeEquals(type, context.getType(boolean.class))) {
+            methodName = "is" + Utils.firstLetterUpperCase(variableName);
+        } else {
+            methodName = "get" + Utils.firstLetterUpperCase(variableName);
+        }
+
+        for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
+            if (method.getSimpleName().toString().equals(methodName) && method.getParameters().size() == 0 && Utils.isAssignable(context, type, method.getReturnType())) {
+                return method;
+            }
+        }
+        return null;
+    }
+
+    private boolean isGenericShortCutMethod(NodeData node, TemplateMethod method) {
+        for (ActualParameter parameter : method.getParameters()) {
+            NodeChildData field = node.findChild(parameter.getSpecification().getName());
+            if (field == null) {
+                continue;
+            }
+            ExecutableTypeData found = null;
+            List<ExecutableTypeData> executableElements = field.findGenericExecutableTypes(context);
+            for (ExecutableTypeData executable : executableElements) {
+                if (executable.getType().equalsType(parameter.getTypeSystemType())) {
+                    found = executable;
+                    break;
+                }
+            }
+            if (found == null) {
+                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 static <M extends TemplateMethod> SortedMap<String, List<M>> groupByNodeId(List<M> methods) {
+        SortedMap<String, List<M>> grouped = new TreeMap<>();
+        for (M m : methods) {
+            List<M> list = grouped.get(m.getId());
+            if (list == null) {
+                list = new ArrayList<>();
+                grouped.put(m.getId(), list);
+            }
+            list.add(m);
+        }
+        return grouped;
+    }
+
+    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;
+    }
+
+}