view 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 source

/*
 * 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;
    }

}