diff graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeFactoryFactory.java @ 18752:1acaa69ff61b

Truffle-DSL: refactor generator classes
author Christian Humer <christian.humer@gmail.com>
date Mon, 29 Dec 2014 23:38:16 +0100
parents
children f6b8787dc113
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/generator/NodeFactoryFactory.java	Mon Dec 29 23:38:16 2014 +0100
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2014, 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.generator;
+
+import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
+import static javax.lang.model.element.Modifier.*;
+
+import java.util.*;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.dsl.processor.java.*;
+import com.oracle.truffle.dsl.processor.java.model.*;
+import com.oracle.truffle.dsl.processor.java.model.CodeTypeMirror.ArrayCodeTypeMirror;
+import com.oracle.truffle.dsl.processor.model.*;
+
+class NodeFactoryFactory extends AbstractClassElementFactory<NodeData> {
+
+    static final String FACTORY_METHOD_NAME = "create0";
+
+    private final Map<NodeData, List<TypeElement>> childTypes;
+    private CodeTypeElement generatedNode;
+
+    NodeFactoryFactory(Map<NodeData, List<TypeElement>> childElements) {
+        this.childTypes = childElements;
+    }
+
+    private static String factoryClassName(NodeData node) {
+        return node.getNodeId() + "Factory";
+    }
+
+    @Override
+    protected CodeTypeElement create(NodeData node) {
+        Modifier visibility = ElementUtils.getVisibility(node.getTemplateType().getModifiers());
+
+        CodeTypeElement clazz = createClass(node, modifiers(), factoryClassName(node), null, false);
+        if (visibility != null) {
+            clazz.getModifiers().add(visibility);
+        }
+        clazz.getModifiers().add(Modifier.FINAL);
+        return clazz;
+    }
+
+    @Override
+    protected void createChildren(NodeData node) {
+        CodeTypeElement clazz = getElement();
+
+        Modifier createVisibility = ElementUtils.getVisibility(clazz.getModifiers());
+
+        if (node.needsFactory()) {
+            NodeBaseFactory factory = new NodeBaseFactory();
+            add(factory, node.getGenericSpecialization() == null ? node.getSpecializations().get(0) : node.getGenericSpecialization());
+            generatedNode = factory.getElement();
+
+            createFactoryMethods(node, clazz, createVisibility);
+
+            for (SpecializationData specialization : node.getSpecializations()) {
+                if (!specialization.isReachable() || specialization.isGeneric()) {
+                    continue;
+                }
+
+                if (specialization.isPolymorphic() && node.isPolymorphic(context)) {
+                    PolymorphicNodeFactory polymorphicFactory = new PolymorphicNodeFactory(generatedNode);
+                    add(polymorphicFactory, specialization);
+                    continue;
+                }
+
+                add(new SpecializedNodeFactory(generatedNode), specialization);
+            }
+
+            TypeMirror nodeFactory = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(getContext().getTruffleTypes().getNodeFactoryBase()), node.getNodeType());
+            clazz.setSuperClass(nodeFactory);
+            clazz.add(createNodeFactoryConstructor(node));
+            clazz.add(createCreateNodeMethod(node));
+            clazz.add(createGetInstanceMethod(node, createVisibility));
+            clazz.add(createInstanceConstant(node, clazz.asType()));
+        }
+
+        for (NodeData childNode : childTypes.keySet()) {
+            if (childNode.getTemplateType().getModifiers().contains(Modifier.PRIVATE)) {
+                continue;
+            }
+
+            for (TypeElement type : childTypes.get(childNode)) {
+                Set<Modifier> typeModifiers = ((CodeTypeElement) type).getModifiers();
+                Modifier visibility = ElementUtils.getVisibility(type.getModifiers());
+                typeModifiers.clear();
+                if (visibility != null) {
+                    typeModifiers.add(visibility);
+                }
+
+                typeModifiers.add(Modifier.STATIC);
+                typeModifiers.add(Modifier.FINAL);
+                clazz.add(type);
+            }
+        }
+
+        List<NodeData> children = node.getNodeDeclaringChildren();
+        if (node.getDeclaringNode() == null && children.size() > 0) {
+            clazz.add(createGetFactories(node));
+        }
+
+    }
+
+    private Element createNodeFactoryConstructor(NodeData node) {
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PRIVATE), null, factoryClassName(node));
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startStatement();
+        builder.startCall("super");
+
+        // node type
+        builder.typeLiteral(node.getNodeType());
+
+        // execution signature
+        builder.startGroup();
+        if (node.getChildExecutions().isEmpty()) {
+            builder.staticReference(context.getTruffleTypes().getDslMetadata(), NodeBaseFactory.EMPTY_CLASS_ARRAY);
+        } else {
+            builder.startNewArray(new ArrayCodeTypeMirror(context.getType(Class.class)), null);
+            for (NodeExecutionData execution : node.getChildExecutions()) {
+                builder.typeLiteral(execution.getNodeType());
+            }
+            builder.end();
+        }
+        builder.end();
+
+        // node signatures
+        builder.startGroup();
+        builder.startNewArray(new ArrayCodeTypeMirror(new ArrayCodeTypeMirror(context.getType(Class.class))), null);
+        List<ExecutableElement> constructors = NodeBaseFactory.findUserConstructors(generatedNode.asType());
+        for (ExecutableElement constructor : constructors) {
+            builder.startGroup();
+            if (constructor.getParameters().isEmpty()) {
+                builder.staticReference(context.getTruffleTypes().getDslMetadata(), NodeBaseFactory.EMPTY_CLASS_ARRAY);
+            } else {
+                builder.startNewArray(new ArrayCodeTypeMirror(context.getType(Class.class)), null);
+                for (VariableElement var : constructor.getParameters()) {
+                    builder.typeLiteral(var.asType());
+                }
+                builder.end();
+            }
+            builder.end();
+        }
+        builder.end();
+        builder.end();
+
+        builder.end().end().end();
+        return method;
+    }
+
+    private CodeExecutableElement createCreateNodeMethod(NodeData node) {
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), node.getNodeType(), "createNode");
+        CodeVariableElement arguments = new CodeVariableElement(getContext().getType(Object.class), "arguments");
+        method.setVarArgs(true);
+        method.addParameter(arguments);
+
+        CodeTreeBuilder builder = method.createBuilder();
+        List<ExecutableElement> signatures = NodeBaseFactory.findUserConstructors(generatedNode.asType());
+        boolean ifStarted = false;
+
+        for (ExecutableElement element : signatures) {
+            ifStarted = builder.startIf(ifStarted);
+            builder.string("arguments.length == " + element.getParameters().size());
+
+            int index = 0;
+            for (VariableElement param : element.getParameters()) {
+                if (ElementUtils.isObject(param.asType())) {
+                    continue;
+                }
+                builder.string(" && ");
+                if (!param.asType().getKind().isPrimitive()) {
+                    builder.string("(arguments[" + index + "] == null || ");
+                }
+                builder.string("arguments[" + index + "] instanceof ");
+                builder.type(ElementUtils.boxType(getContext(), param.asType()));
+                if (!param.asType().getKind().isPrimitive()) {
+                    builder.string(")");
+                }
+                index++;
+            }
+            builder.end();
+            builder.startBlock();
+
+            builder.startReturn().startCall("create");
+            index = 0;
+            for (VariableElement param : element.getParameters()) {
+                builder.startGroup();
+                if (!ElementUtils.isObject(param.asType())) {
+                    builder.string("(").type(param.asType()).string(") ");
+                }
+                builder.string("arguments[").string(String.valueOf(index)).string("]");
+                builder.end();
+                index++;
+            }
+            builder.end().end();
+
+            builder.end(); // block
+        }
+
+        builder.startElseBlock();
+        builder.startThrow().startNew(getContext().getType(IllegalArgumentException.class));
+        builder.doubleQuote("Invalid create signature.");
+        builder.end().end();
+        builder.end(); // else block
+        return method;
+    }
+
+    private ExecutableElement createGetInstanceMethod(NodeData node, Modifier visibility) {
+        TypeElement nodeFactoryType = ElementUtils.fromTypeMirror(getContext().getType(NodeFactory.class));
+        TypeMirror returnType = ElementUtils.getDeclaredType(nodeFactoryType, node.getNodeType());
+
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(), returnType, "getInstance");
+        if (visibility != null) {
+            method.getModifiers().add(visibility);
+        }
+        method.getModifiers().add(Modifier.STATIC);
+
+        String varName = instanceVarName(node);
+
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startIf();
+        builder.string(varName).string(" == null");
+        builder.end().startBlock();
+
+        builder.startStatement();
+        builder.string(varName);
+        builder.string(" = ");
+        builder.startNew(factoryClassName(node)).end();
+        builder.end();
+
+        builder.end();
+        builder.startReturn().string(varName).end();
+        return method;
+    }
+
+    private static String instanceVarName(NodeData node) {
+        if (node.getDeclaringNode() != null) {
+            return ElementUtils.firstLetterLowerCase(factoryClassName(node)) + "Instance";
+        } else {
+            return "instance";
+        }
+    }
+
+    private static CodeVariableElement createInstanceConstant(NodeData node, TypeMirror factoryType) {
+        String varName = instanceVarName(node);
+        CodeVariableElement var = new CodeVariableElement(modifiers(), factoryType, varName);
+        var.getModifiers().add(Modifier.PRIVATE);
+        var.getModifiers().add(Modifier.STATIC);
+        return var;
+    }
+
+    private ExecutableElement createGetFactories(NodeData node) {
+        List<NodeData> children = node.getNodeDeclaringChildren();
+        if (node.needsFactory()) {
+            children.add(node);
+        }
+
+        List<TypeMirror> nodeTypesList = new ArrayList<>();
+        TypeMirror prev = null;
+        boolean allSame = true;
+        for (NodeData child : children) {
+            nodeTypesList.add(child.getNodeType());
+            if (prev != null && !ElementUtils.typeEquals(child.getNodeType(), prev)) {
+                allSame = false;
+            }
+            prev = child.getNodeType();
+        }
+        TypeMirror commonNodeSuperType = ElementUtils.getCommonSuperType(getContext(), nodeTypesList.toArray(new TypeMirror[nodeTypesList.size()]));
+
+        Types types = getContext().getEnvironment().getTypeUtils();
+        TypeMirror factoryType = getContext().getType(NodeFactory.class);
+        TypeMirror baseType;
+        if (allSame) {
+            baseType = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(factoryType), commonNodeSuperType);
+        } else {
+            baseType = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(factoryType), types.getWildcardType(commonNodeSuperType, null));
+        }
+        TypeMirror listType = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(getContext().getType(List.class)), baseType);
+
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), listType, "getFactories");
+
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startReturn();
+        builder.startStaticCall(getContext().getType(Arrays.class), "asList");
+
+        for (NodeData child : children) {
+            builder.startGroup();
+            NodeData childNode = child;
+            List<NodeData> factories = new ArrayList<>();
+            while (childNode.getDeclaringNode() != null) {
+                factories.add(childNode);
+                childNode = childNode.getDeclaringNode();
+            }
+            Collections.reverse(factories);
+            for (NodeData nodeData : factories) {
+                builder.string(factoryClassName(nodeData)).string(".");
+            }
+            builder.string("getInstance()");
+            builder.end();
+        }
+        builder.end();
+        builder.end();
+        return method;
+    }
+
+    private void createFactoryMethods(NodeData node, CodeTypeElement clazz, Modifier createVisibility) {
+        List<ExecutableElement> constructors = NodeBaseFactory.findUserConstructors(generatedNode.asType());
+        for (ExecutableElement constructor : constructors) {
+            clazz.add(createCreateMethod(node, createVisibility, constructor));
+        }
+    }
+
+    private CodeExecutableElement createCreateMethod(NodeData node, Modifier visibility, ExecutableElement constructor) {
+        CodeExecutableElement method = CodeExecutableElement.clone(getContext().getEnvironment(), constructor);
+        method.setSimpleName(CodeNames.of("create"));
+        method.getModifiers().clear();
+        if (visibility != null) {
+            method.getModifiers().add(visibility);
+        }
+        method.getModifiers().add(Modifier.STATIC);
+        method.setReturnType(node.getNodeType());
+
+        CodeTreeBuilder body = method.createBuilder();
+        body.startReturn();
+        if (node.getSpecializations().isEmpty()) {
+            body.nullLiteral();
+        } else {
+            body.startCall(NodeBaseFactory.nodeSpecializationClassName(node.getSpecializations().get(0)), FACTORY_METHOD_NAME);
+            for (VariableElement var : method.getParameters()) {
+                body.string(var.getSimpleName().toString());
+            }
+            body.end();
+        }
+        body.end();
+        return method;
+    }
+
+}
\ No newline at end of file