Mercurial > hg > graal-compiler
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