diff graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java @ 18776:c0fb70634640

Truffle-DSL: support for frame types Frame, MaterializedFrame. Added validation for frame type consistency. Some refactorings along the way.
author Christian Humer <christian.humer@gmail.com>
date Mon, 05 Jan 2015 01:31:08 +0100
parents a069a87b9a02
children 941761f6b736
line wrap: on
line diff
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Mon Jan 05 01:31:08 2015 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Mon Jan 05 01:31:08 2015 +0100
@@ -45,25 +45,13 @@
     public static final List<Class<? extends Annotation>> ANNOTATIONS = Arrays.asList(Fallback.class, TypeSystemReference.class, ShortCircuit.class, Specialization.class, NodeChild.class,
                     NodeChildren.class);
 
-    private Map<String, NodeData> parsedNodes;
-
     @Override
     protected NodeData parse(Element element, AnnotationMirror mirror) {
-        NodeData node = null;
-        try {
-            parsedNodes = new HashMap<>();
-            node = resolveNode((TypeElement) element);
-            if (Log.DEBUG) {
-                NodeData parsed = parsedNodes.get(ElementUtils.getQualifiedName((TypeElement) element));
-                if (node != null) {
-                    String dump = parsed.dump();
-                    log.message(Kind.ERROR, null, null, null, dump);
-                }
-            }
-        } finally {
-            parsedNodes = null;
+        NodeData node = parseRootType((TypeElement) element);
+        if (Log.isDebug() && node != null) {
+            String dump = node.dump();
+            log.message(Kind.ERROR, null, null, null, dump);
         }
-
         return node;
     }
 
@@ -96,20 +84,14 @@
         return ANNOTATIONS;
     }
 
-    private NodeData resolveNode(TypeElement rootType) {
-        String typeName = ElementUtils.getQualifiedName(rootType);
-        if (parsedNodes.containsKey(typeName)) {
-            return parsedNodes.get(typeName);
-        }
-
+    private NodeData parseRootType(TypeElement rootType) {
         List<NodeData> enclosedNodes = new ArrayList<>();
         for (TypeElement enclosedType : ElementFilter.typesIn(rootType.getEnclosedElements())) {
-            NodeData enclosedChild = resolveNode(enclosedType);
+            NodeData enclosedChild = parseRootType(enclosedType);
             if (enclosedChild != null) {
                 enclosedNodes.add(enclosedChild);
             }
         }
-
         NodeData node = parseNode(rootType);
         if (node == null && !enclosedNodes.isEmpty()) {
             node = new NodeData(context, rootType);
@@ -120,8 +102,6 @@
                 node.addEnclosedNode(enclosedNode);
             }
         }
-
-        parsedNodes.put(typeName, node);
         return node;
     }
 
@@ -134,24 +114,64 @@
             return null;
         }
 
-        List<TypeElement> lookupTypes = collectSuperClasses(new ArrayList<TypeElement>(), templateType);
         if (!ElementUtils.isAssignable(templateType.asType(), context.getTruffleTypes().getNode())) {
             return null;
         }
-        List<Element> elements = new ArrayList<>(CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType));
+
+        List<TypeElement> lookupTypes = collectSuperClasses(new ArrayList<TypeElement>(), templateType);
+        List<Element> members = loadMembers(templateType);
+        // ensure the processed element has at least one @Specialization annotation.
+        if (!containsSpecializations(members)) {
+            return null;
+        }
 
-        NodeData node = parseNodeData(templateType, elements, lookupTypes);
+        NodeData node = parseNodeData(templateType, lookupTypes);
 
-        parseImportGuards(node, lookupTypes, elements);
+        node.getAssumptions().addAll(parseAssumptions(lookupTypes));
+        node.getFields().addAll(parseFields(lookupTypes, members));
+        node.getChildren().addAll(parseChildren(lookupTypes, members));
+        node.getChildExecutions().addAll(parseExecutions(node.getChildren(), members));
+        node.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, node, context.getFrameTypes()).parse(members)));
+
+        initializeExecutableTypes(node);
+        initializeImportGuards(node, lookupTypes, members);
 
         if (node.hasErrors()) {
             return node; // error sync point
         }
 
-        initializeExecutableTypes(elements, node);
         initializeChildren(node);
 
-        // ensure the processed element has at least one @Specialization annotation.
+        if (node.hasErrors()) {
+            return node; // error sync point
+        }
+
+        node.getSpecializations().addAll(new SpecializationMethodParser(context, node).parse(members));
+        node.getSpecializations().addAll(new GenericParser(context, node).parse(members));
+        node.getCasts().addAll(new CreateCastParser(context, node).parse(members));
+        node.getShortCircuits().addAll(new ShortCircuitParser(context, node).parse(members));
+
+        if (node.hasErrors()) {
+            return node;  // error sync point
+        }
+
+        verifySpecializationSameLength(node);
+        initializeSpecializations(members, node);
+        initializeShortCircuits(node); // requires specializations and polymorphic specializations
+
+        verifyVisibilities(node);
+        verifyMissingAbstractMethods(node, members);
+        verifyConstructors(node);
+        verifyNamingConvention(node.getShortCircuits(), "needs");
+        verifySpecializationThrows(node);
+        return node;
+    }
+
+    private ArrayList<Element> loadMembers(TypeElement templateType) {
+        return new ArrayList<>(CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType));
+    }
+
+    private boolean containsSpecializations(List<Element> elements) {
         boolean foundSpecialization = false;
         for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
             if (ElementUtils.findAnnotationMirror(processingEnv, method, Specialization.class) != null) {
@@ -159,36 +179,10 @@
                 break;
             }
         }
-        if (!foundSpecialization) {
-            return node;
-        }
-
-        if (node.hasErrors()) {
-            return node; // error sync point
-        }
-
-        node.getSpecializations().addAll(new SpecializationMethodParser(context, node).parse(elements));
-        node.getSpecializations().addAll(new GenericParser(context, node).parse(elements));
-        node.getCasts().addAll(new CreateCastParser(context, node).parse(elements));
-        node.getShortCircuits().addAll(new ShortCircuitParser(context, node).parse(elements));
-
-        if (node.hasErrors()) {
-            return node;  // error sync point
-        }
-
-        verifySpecializationSameLength(node);
-        initializeSpecializations(elements, node);
-        initializeShortCircuits(node); // requires specializations and polymorphic specializations
-
-        verifyVisibilities(node);
-        verifyMissingAbstractMethods(node, elements);
-        verifyConstructors(node);
-        verifyNamingConvention(node.getShortCircuits(), "needs");
-        verifySpecializationThrows(node);
-        return node;
+        return foundSpecialization;
     }
 
-    private void parseImportGuards(NodeData node, List<TypeElement> lookupTypes, List<Element> elements) {
+    private void initializeImportGuards(NodeData node, List<TypeElement> lookupTypes, List<Element> elements) {
         for (TypeElement lookupType : lookupTypes) {
             AnnotationMirror importAnnotation = ElementUtils.findAnnotationMirror(processingEnv, lookupType, ImportGuards.class);
             if (importAnnotation == null) {
@@ -228,7 +222,7 @@
         }
     }
 
-    private NodeData parseNodeData(TypeElement templateType, List<? extends Element> elements, List<TypeElement> typeHierarchy) {
+    private NodeData parseNodeData(TypeElement templateType, List<TypeElement> typeHierarchy) {
         AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class);
         if (typeSystemMirror == null) {
             NodeData nodeData = new NodeData(context, templateType);
@@ -244,6 +238,17 @@
             return nodeData;
         }
 
+        AnnotationMirror nodeInfoMirror = findFirstAnnotation(typeHierarchy, NodeInfo.class);
+        String shortName = null;
+        if (nodeInfoMirror != null) {
+            shortName = ElementUtils.getAnnotationValue(String.class, nodeInfoMirror, "shortName");
+        }
+        boolean useNodeFactory = findFirstAnnotation(typeHierarchy, GenerateNodeFactory.class) != null;
+        return new NodeData(context, templateType, shortName, typeSystem, useNodeFactory);
+
+    }
+
+    private List<String> parseAssumptions(List<TypeElement> typeHierarchy) {
         List<String> assumptionsList = new ArrayList<>();
         for (int i = typeHierarchy.size() - 1; i >= 0; i--) {
             TypeElement type = typeHierarchy.get(i);
@@ -258,22 +263,7 @@
                 }
             }
         }
-        AnnotationMirror nodeInfoMirror = findFirstAnnotation(typeHierarchy, NodeInfo.class);
-        String shortName = null;
-        if (nodeInfoMirror != null) {
-            shortName = ElementUtils.getAnnotationValue(String.class, nodeInfoMirror, "shortName");
-        }
-
-        List<NodeFieldData> fields = parseFields(typeHierarchy, elements);
-        List<NodeChildData> children = parseChildren(typeHierarchy, elements);
-        List<NodeExecutionData> executions = parseExecutions(children, elements);
-        boolean useNodeFactory = findFirstAnnotation(typeHierarchy, GenerateNodeFactory.class) != null;
-
-        NodeData nodeData = new NodeData(context, templateType, shortName, typeSystem, children, executions, fields, assumptionsList, useNodeFactory);
-
-        parsedNodes.put(ElementUtils.getQualifiedName(templateType), nodeData);
-
-        return nodeData;
+        return assumptionsList;
     }
 
     private List<NodeFieldData> parseFields(List<TypeElement> typeHierarchy, List<? extends Element> elements) {
@@ -469,31 +459,33 @@
             }
         }
 
-        // pre-parse specializations
+        List<TypeMirror> frameTypes = context.getFrameTypes();
+        // pre-parse specializations to find signature size
         for (ExecutableElement method : methods) {
             AnnotationMirror mirror = ElementUtils.findAnnotationMirror(processingEnv, method, Specialization.class);
             if (mirror == null) {
                 continue;
             }
 
-            int currentArgumentCount = 0;
+            int currentArgumentIndex = 0;
             boolean skipShortCircuit = false;
-            for (VariableElement var : method.getParameters()) {
+            outer: for (VariableElement var : method.getParameters()) {
                 TypeMirror type = var.asType();
-                if (currentArgumentCount == 0) {
+                if (currentArgumentIndex == 0) {
                     // skip optionals
-                    if (ElementUtils.typeEquals(type, context.getTruffleTypes().getFrame())) {
-                        continue;
+                    for (TypeMirror frameType : frameTypes) {
+                        if (ElementUtils.typeEquals(type, frameType)) {
+                            continue outer;
+                        }
                     }
-                    // TODO skip optional fields?
                 }
-                int childIndex = currentArgumentCount < children.size() ? currentArgumentCount : children.size() - 1;
+                int childIndex = currentArgumentIndex < children.size() ? currentArgumentIndex : children.size() - 1;
                 if (childIndex == -1) {
                     continue;
                 }
                 if (!skipShortCircuit) {
                     NodeChildData child = children.get(childIndex);
-                    if (shortCircuits.contains(NodeExecutionData.createShortCircuitId(child, currentArgumentCount - childIndex))) {
+                    if (shortCircuits.contains(NodeExecutionData.createShortCircuitId(child, currentArgumentIndex - childIndex))) {
                         skipShortCircuit = true;
                         continue;
                     }
@@ -501,9 +493,9 @@
                     skipShortCircuit = false;
                 }
 
-                currentArgumentCount++;
+                currentArgumentIndex++;
             }
-            maxSignatureSize = Math.max(maxSignatureSize, currentArgumentCount);
+            maxSignatureSize = Math.max(maxSignatureSize, currentArgumentIndex);
         }
 
         List<NodeExecutionData> executions = new ArrayList<>();
@@ -526,25 +518,63 @@
         return executions;
     }
 
-    private void initializeExecutableTypes(List<Element> elements, NodeData node) {
-        node.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, node).parse(elements)));
-        List<ExecutableTypeData> genericExecutes = node.getThisExecution().getChild().findGenericExecutableTypes(context);
+    private void initializeExecutableTypes(NodeData node) {
+        List<ExecutableTypeData> genericExecutes = node.findGenericExecutableTypes(context, 0);
+        List<ExecutableTypeData> allExecutes = node.getExecutableTypes();
+
+        Set<String> inconsistentFrameTypes = new HashSet<>();
+        TypeMirror frameType = null;
+        Set<Integer> evaluatedCounts = new HashSet<>();
+        for (ExecutableTypeData execute : allExecutes) {
+            evaluatedCounts.add(execute.getEvaluatedCount());
+
+            Parameter frame = execute.getFrame();
+            TypeMirror resolvedFrameType;
+            if (frame == null) {
+                resolvedFrameType = node.getTypeSystem().getVoidType().getPrimitiveType();
+            } else {
+                resolvedFrameType = frame.getType();
+            }
 
-        List<ExecutableTypeData> overridableGenericExecutes = new ArrayList<>();
+            if (frameType == null) {
+                frameType = resolvedFrameType;
+            } else {
+                if (!ElementUtils.typeEquals(frameType, resolvedFrameType)) {
+                    // found inconsistent frame types
+                    inconsistentFrameTypes.add(ElementUtils.getSimpleName(frameType));
+                    inconsistentFrameTypes.add(ElementUtils.getSimpleName(resolvedFrameType));
+                }
+            }
+        }
+        if (!inconsistentFrameTypes.isEmpty()) {
+            // ensure they are sorted somehow
+            List<String> inconsistentFrameTypesList = new ArrayList<>(inconsistentFrameTypes);
+            Collections.sort(inconsistentFrameTypesList);
+            node.addError("Invalid inconsistent frame types %s found for the declared execute methods. The frame type must be identical for all execute methods.", inconsistentFrameTypesList);
+        }
+        node.setFrameType(frameType);
+
+        int nonFinalCount = 0;
+        int voidCount = 0;
         for (ExecutableTypeData executableTypeData : genericExecutes) {
             if (!executableTypeData.getMethod().getModifiers().contains(Modifier.FINAL)) {
-                overridableGenericExecutes.add(executableTypeData);
+                nonFinalCount++;
+            }
+            if (ElementUtils.isVoid(executableTypeData.getReturnType().getType())) {
+                voidCount++;
             }
         }
 
-        if (overridableGenericExecutes.isEmpty()) {
+        // no generic executes
+        if (nonFinalCount == 0 && voidCount == 0) {
             node.addError("No accessible and overridable generic execute method found. Generic execute methods usually have the "
-                            + "signature 'public abstract {Type} executeGeneric(VirtualFrame)' and must not throw any checked exceptions.");
+                            + "signature 'public abstract {Type} execute(VirtualFrame)' and must not throw any checked exceptions.");
         }
 
-        if (overridableGenericExecutes.size() > 1) {
+        // multiple generic execute
+        if (voidCount > 1 || (nonFinalCount - voidCount) > 1) {
             List<String> methodSignatures = new ArrayList<>();
-            for (ExecutableTypeData type : overridableGenericExecutes) {
+            for (ExecutableTypeData type : genericExecutes) {
                 methodSignatures.add(type.createReferenceName());
             }
             node.addWarning("Multiple accessible and overridable generic execute methods found %s. Remove all but one or mark all but one as final.", methodSignatures);
@@ -571,30 +601,61 @@
     }
 
     private void initializeChildren(NodeData node) {
-        for (NodeChildData nodeChild : node.getChildren()) {
-            NodeData fieldNodeData = resolveNode(ElementUtils.fromTypeMirror(nodeChild.getNodeType()));
-            nodeChild.setNode(fieldNodeData);
-            if (fieldNodeData == null) {
-                nodeChild.addError("Node type '%s' is invalid or not a valid Node.", ElementUtils.getQualifiedName(nodeChild.getNodeType()));
+        for (NodeChildData child : node.getChildren()) {
+            TypeMirror nodeType = child.getNodeType();
+            NodeData fieldNodeData = parseChildNodeData(node, ElementUtils.fromTypeMirror(nodeType));
+
+            child.setNode(fieldNodeData);
+            if (fieldNodeData == null || fieldNodeData.hasErrors()) {
+                child.addError("Node type '%s' is invalid or not a subclass of Node.", ElementUtils.getQualifiedName(nodeType));
             } else if (!ElementUtils.typeEquals(fieldNodeData.getTypeSystem().getTemplateType().asType(), (node.getTypeSystem().getTemplateType().asType()))) {
-                nodeChild.addError("The @%s of the node and the @%s of the @%s does not match. %s != %s. ", TypeSystem.class.getSimpleName(), TypeSystem.class.getSimpleName(),
+                child.addError("The @%s of the node and the @%s of the @%s does not match. %s != %s. ", TypeSystem.class.getSimpleName(), TypeSystem.class.getSimpleName(),
                                 NodeChild.class.getSimpleName(), ElementUtils.getSimpleName(node.getTypeSystem().getTemplateType()),
                                 ElementUtils.getSimpleName(fieldNodeData.getTypeSystem().getTemplateType()));
-            } else if (nodeChild.findAnyGenericExecutableType(context) == null) {
-                nodeChild.addError("No generic execute method found for child type %s. Generic execute methods usually have the signature 'Object executeGeneric(VirtualFrame)'.",
-                                ElementUtils.getQualifiedName(nodeChild.getNodeType()));
-            }
-            if (fieldNodeData != null) {
-                List<ExecutableTypeData> types = nodeChild.findGenericExecutableTypes(context);
+            } else {
+                List<ExecutableTypeData> types = child.findGenericExecutableTypes(context);
                 if (types.isEmpty()) {
-                    AnnotationValue executeWithValue = ElementUtils.getAnnotationValue(nodeChild.getMessageAnnotation(), "executeWith");
-                    nodeChild.addError(executeWithValue, "No generic execute method found with %s evaluated arguments for node type %s.", nodeChild.getExecuteWith().size(),
-                                    ElementUtils.getSimpleName(nodeChild.getNodeType()));
+                    AnnotationValue executeWithValue = ElementUtils.getAnnotationValue(child.getMessageAnnotation(), "executeWith");
+                    child.addError(executeWithValue, "No generic execute method found with %s evaluated arguments for node type %s and frame types %s.", child.getExecuteWith().size(),
+                                    ElementUtils.getSimpleName(nodeType), ElementUtils.getUniqueIdentifiers(createAllowedChildFrameTypes(node)));
                 }
             }
         }
     }
 
+    private NodeData parseChildNodeData(NodeData parentNode, TypeElement originalTemplateType) {
+        TypeElement templateType = ElementUtils.fromTypeMirror(context.reloadTypeElement(originalTemplateType));
+
+        if (ElementUtils.findAnnotationMirror(processingEnv, originalTemplateType, GeneratedBy.class) != null) {
+            // generated nodes should not get called again.
+            return null;
+        }
+
+        if (!ElementUtils.isAssignable(templateType.asType(), context.getTruffleTypes().getNode())) {
+            return null;
+        }
+
+        List<TypeElement> lookupTypes = collectSuperClasses(new ArrayList<TypeElement>(), templateType);
+
+        // Declaration order is not required for child nodes.
+        List<? extends Element> members = processingEnv.getElementUtils().getAllMembers(templateType);
+        NodeData node = parseNodeData(templateType, lookupTypes);
+
+        node.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, node, createAllowedChildFrameTypes(parentNode)).parse(members)));
+        node.setFrameType(parentNode.getFrameType());
+        return node;
+    }
+
+    private List<TypeMirror> createAllowedChildFrameTypes(NodeData parentNode) {
+        List<TypeMirror> allowedFrameTypes = new ArrayList<>();
+        for (TypeMirror frameType : context.getFrameTypes()) {
+            if (ElementUtils.isAssignable(parentNode.getFrameType(), frameType)) {
+                allowedFrameTypes.add(frameType);
+            }
+        }
+        return allowedFrameTypes;
+    }
+
     private void initializeSpecializations(List<? extends Element> elements, final NodeData node) {
         if (node.getSpecializations().isEmpty()) {
             return;