/* * 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.modifiers; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; import java.util.Arrays; import java.util.List; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.dsl.processor.ProcessorContext; import com.oracle.truffle.dsl.processor.java.ElementUtils; import com.oracle.truffle.dsl.processor.java.model.CodeAnnotationMirror; import com.oracle.truffle.dsl.processor.java.model.CodeAnnotationValue; import com.oracle.truffle.dsl.processor.java.model.CodeExecutableElement; import com.oracle.truffle.dsl.processor.java.model.CodeNames; import com.oracle.truffle.dsl.processor.java.model.CodeTreeBuilder; import com.oracle.truffle.dsl.processor.java.model.CodeTypeElement; import com.oracle.truffle.dsl.processor.java.model.CodeVariableElement; import com.oracle.truffle.dsl.processor.model.NodeData; import com.oracle.truffle.dsl.processor.model.NodeExecutionData; class NodeFactoryFactory { private final ProcessorContext context; private final NodeData node; private final CodeTypeElement createdFactoryElement; NodeFactoryFactory(ProcessorContext context, NodeData node, CodeTypeElement createdClass) { this.context = context; this.node = node; this.createdFactoryElement = createdClass; } public static String factoryClassName(NodeData node) { return node.getNodeId() + "Factory"; } public CodeTypeElement create() { Modifier visibility = ElementUtils.getVisibility(node.getTemplateType().getModifiers()); TypeMirror nodeFactory = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(context.getType(NodeFactory.class)), node.getNodeType()); CodeTypeElement clazz = GeneratorUtils.createClass(node, null, modifiers(), factoryClassName(node), null); if (visibility != null) { clazz.getModifiers().add(visibility); } clazz.getModifiers().add(Modifier.FINAL); if (createdFactoryElement != null) { clazz.getImplements().add(nodeFactory); CodeAnnotationMirror supressWarnings = new CodeAnnotationMirror(context.getDeclaredType(SuppressWarnings.class)); supressWarnings.setElementValue(supressWarnings.findExecutableElement("value"), new CodeAnnotationValue(Arrays.asList(new CodeAnnotationValue("unchecked"), new CodeAnnotationValue("rawtypes")))); clazz.getAnnotationMirrors().add(supressWarnings); clazz.add(createNodeFactoryConstructor()); clazz.add(createCreateGetNodeClass()); clazz.add(createCreateGetExecutionSignature()); clazz.add(createCreateGetNodeSignatures()); clazz.add(createCreateNodeMethod()); clazz.add(createGetInstanceMethod(visibility)); clazz.add(createInstanceConstant(clazz.asType())); createFactoryMethods(clazz); } return clazz; } private Element createNodeFactoryConstructor() { CodeExecutableElement method = new CodeExecutableElement(modifiers(PRIVATE), null, factoryClassName(node)); return method; } private CodeExecutableElement createCreateGetNodeClass() { TypeMirror returnValue = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(context.getType(Class.class)), node.getNodeType()); CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnValue, "getNodeClass"); method.createBuilder().startReturn().typeLiteral(node.getNodeType()).end(); return method; } private CodeExecutableElement createCreateGetNodeSignatures() { TypeMirror returnValue = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(context.getType(List.class))); CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnValue, "getNodeSignatures"); CodeTreeBuilder builder = method.createBuilder(); builder.startReturn(); builder.startGroup(); builder.startStaticCall(context.getType(Arrays.class), "asList"); List<ExecutableElement> constructors = GeneratorUtils.findUserConstructors(createdFactoryElement.asType()); for (ExecutableElement constructor : constructors) { builder.startGroup(); builder.startStaticCall(context.getType(Arrays.class), "asList"); for (VariableElement var : constructor.getParameters()) { builder.typeLiteral(var.asType()); } builder.end(); builder.end(); } builder.end(); builder.end(); builder.end(); return method; } private CodeExecutableElement createCreateGetExecutionSignature() { TypeMirror returnValue = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(context.getType(List.class))); CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnValue, "getExecutionSignature"); CodeTreeBuilder builder = method.createBuilder(); builder.startReturn(); builder.startStaticCall(context.getType(Arrays.class), "asList"); for (NodeExecutionData execution : node.getChildExecutions()) { builder.typeLiteral(execution.getNodeType()); } builder.end(); builder.end(); return method; } private CodeExecutableElement createCreateNodeMethod() { CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), node.getNodeType(), "createNode"); CodeVariableElement arguments = new CodeVariableElement(context.getType(Object.class), "arguments"); method.setVarArgs(true); method.addParameter(arguments); CodeTreeBuilder builder = method.createBuilder(); List<ExecutableElement> signatures = GeneratorUtils.findUserConstructors(createdFactoryElement.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())) { index++; continue; } builder.string(" && "); if (!param.asType().getKind().isPrimitive()) { builder.string("(arguments[" + index + "] == null || "); } builder.string("arguments[" + index + "] instanceof "); builder.type(ElementUtils.boxType(context, 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(context.getType(IllegalArgumentException.class)); builder.doubleQuote("Invalid create signature."); builder.end().end(); builder.end(); // else block return method; } private ExecutableElement createGetInstanceMethod(Modifier visibility) { TypeElement nodeFactoryType = ElementUtils.fromTypeMirror(context.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 CodeVariableElement createInstanceConstant(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; } public void createFactoryMethods(CodeTypeElement clazz) { List<ExecutableElement> constructors = GeneratorUtils.findUserConstructors(createdFactoryElement.asType()); for (ExecutableElement constructor : constructors) { clazz.add(createCreateMethod(constructor)); if (constructor instanceof CodeExecutableElement) { ElementUtils.setVisibility(constructor.getModifiers(), Modifier.PRIVATE); } } } private CodeExecutableElement createCreateMethod(ExecutableElement constructor) { CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), constructor); method.setSimpleName(CodeNames.of("create")); method.getModifiers().clear(); method.getModifiers().add(Modifier.PUBLIC); method.getModifiers().add(Modifier.STATIC); method.setReturnType(node.getNodeType()); CodeTreeBuilder body = method.createBuilder(); body.startReturn(); if (node.getSpecializations().isEmpty()) { body.nullLiteral(); } else { body.startNew(NodeCodeGenerator.nodeType(node)); for (VariableElement var : method.getParameters()) { body.string(var.getSimpleName().toString()); } body.end(); } body.end(); return method; } }