/*
* Copyright (c) 2012, 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.FINAL;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Types;
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.CodeExecutableElement;
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.java.model.GeneratedTypeMirror;
import com.oracle.truffle.dsl.processor.model.MessageContainer.Message;
import com.oracle.truffle.dsl.processor.model.NodeChildData;
import com.oracle.truffle.dsl.processor.model.NodeData;
public class NodeCodeGenerator extends CodeTypeElementFactory<NodeData> {
@Override
public CodeTypeElement create(ProcessorContext context, NodeData node) {
List<CodeTypeElement> enclosedTypes = new ArrayList<>();
for (NodeData childNode : node.getEnclosingNodes()) {
CodeTypeElement type = create(context, childNode);
if (type != null) {
enclosedTypes.add(type);
}
}
List<CodeTypeElement> generatedNodes = generateNodes(context, node);
if (!generatedNodes.isEmpty() || !enclosedTypes.isEmpty()) {
CodeTypeElement type;
if (generatedNodes.isEmpty()) {
type = createContainer(node);
} else {
type = wrapGeneratedNodes(context, node, generatedNodes);
}
for (CodeTypeElement enclosedFactory : enclosedTypes) {
type.add(makeInnerClass(enclosedFactory));
}
if (node.getDeclaringNode() == null && enclosedTypes.size() > 0) {
ExecutableElement getFactories = createGetFactories(context, node);
if (getFactories != null) {
type.add(getFactories);
}
}
return type;
} else {
return null;
}
}
private static CodeTypeElement makeInnerClass(CodeTypeElement type) {
Set<Modifier> modifiers = type.getModifiers();
modifiers.add(Modifier.STATIC);
return type;
}
private static CodeTypeElement wrapGeneratedNodes(ProcessorContext context, NodeData node, List<CodeTypeElement> generatedNodes) {
if (node.isGenerateFactory()) {
// wrap all types into a generated factory
CodeTypeElement factoryElement = new NodeFactoryFactory(context, node, generatedNodes.get(0)).create();
for (CodeTypeElement generatedNode : generatedNodes) {
factoryElement.add(makeInnerClass(generatedNode));
}
return factoryElement;
} else {
// wrap all types into the first node
CodeTypeElement first = generatedNodes.get(0);
CodeTypeElement second = first;
if (generatedNodes.size() > 1) {
second = generatedNodes.get(1);
for (CodeTypeElement generatedNode : generatedNodes) {
if (first != generatedNode) {
first.add(makeInnerClass(generatedNode));
}
}
}
new NodeFactoryFactory(context, node, second).createFactoryMethods(first);
ElementUtils.setVisibility(first.getModifiers(), ElementUtils.getVisibility(node.getTemplateType().getModifiers()));
return first;
}
}
private static CodeTypeElement createContainer(NodeData node) {
CodeTypeElement container;
Modifier visibility = ElementUtils.getVisibility(node.getTemplateType().getModifiers());
String containerName = NodeFactoryFactory.factoryClassName(node);
container = GeneratorUtils.createClass(node, null, modifiers(), containerName, null);
if (visibility != null) {
container.getModifiers().add(visibility);
}
container.getModifiers().add(Modifier.FINAL);
return container;
}
private static String getAccessorClassName(NodeData node) {
return node.isGenerateFactory() ? NodeFactoryFactory.factoryClassName(node) : createNodeTypeName(node);
}
public static TypeMirror nodeType(NodeData node) {
return new GeneratedTypeMirror(ElementUtils.getPackageName(node.getTemplateType()), createNodeTypeName(node));
}
private static final String NODE_SUFFIX = "NodeGen";
private static String resolveNodeId(NodeData node) {
String nodeid = node.getNodeId();
if (nodeid.endsWith("Node") && !nodeid.equals("Node")) {
nodeid = nodeid.substring(0, nodeid.length() - 4);
}
return nodeid;
}
public static String createNodeTypeName(NodeData node) {
return resolveNodeId(node) + NODE_SUFFIX;
}
@SuppressWarnings("deprecation")
private static List<CodeTypeElement> generateNodes(ProcessorContext context, NodeData node) {
if (!node.needsFactory()) {
return Collections.emptyList();
}
CodeTypeElement type = GeneratorUtils.createClass(node, null, modifiers(FINAL), createNodeTypeName(node), node.getTemplateType().asType());
ElementUtils.setVisibility(type.getModifiers(), ElementUtils.getVisibility(node.getTemplateType().getModifiers()));
if (node.hasErrors()) {
generateErrorNode(context, node, type);
return Arrays.asList(type);
}
switch (node.getTypeSystem().getOptions().defaultGenerator()) {
case FLAT:
type = new FlatNodeGenFactory(context, node).create(type);
break;
case DEFAULT:
type = new DefaultNodeGenFactory(context, node).create(type);
break;
default:
throw new AssertionError();
}
return Arrays.asList(type);
}
private static void generateErrorNode(ProcessorContext context, NodeData node, CodeTypeElement type) {
for (ExecutableElement superConstructor : GeneratorUtils.findUserConstructors(node.getTemplateType().asType())) {
CodeExecutableElement constructor = GeneratorUtils.createConstructorUsingFields(modifiers(), type, superConstructor);
ElementUtils.setVisibility(constructor.getModifiers(), ElementUtils.getVisibility(superConstructor.getModifiers()));
List<CodeVariableElement> childParameters = new ArrayList<>();
for (NodeChildData child : node.getChildren()) {
childParameters.add(new CodeVariableElement(child.getOriginalType(), child.getName()));
}
constructor.getParameters().addAll(superConstructor.getParameters().size(), childParameters);
type.add(constructor);
}
for (ExecutableElement method : ElementFilter.methodsIn(context.getEnvironment().getElementUtils().getAllMembers(node.getTemplateType()))) {
if (method.getModifiers().contains(Modifier.ABSTRACT) && ElementUtils.getVisibility(method.getModifiers()) != Modifier.PRIVATE) {
CodeExecutableElement overrideMethod = CodeExecutableElement.clone(context.getEnvironment(), method);
overrideMethod.getModifiers().remove(Modifier.ABSTRACT);
List<Message> messages = node.collectMessages();
String message = messages.toString();
message = message.replaceAll("\"", "\\\\\"");
message = message.replaceAll("\n", "\\\\n");
overrideMethod.createBuilder().startThrow().startNew(context.getType(RuntimeException.class)).doubleQuote("Truffle DSL compiler errors: " + message).end().end();
type.add(overrideMethod);
}
}
}
private static ExecutableElement createGetFactories(ProcessorContext context, NodeData node) {
List<NodeData> factoryList = node.getNodesWithFactories();
if (node.needsFactory() && node.isGenerateFactory()) {
factoryList.add(node);
}
if (factoryList.isEmpty()) {
return null;
}
List<TypeMirror> nodeTypesList = new ArrayList<>();
TypeMirror prev = null;
boolean allSame = true;
for (NodeData child : factoryList) {
nodeTypesList.add(child.getNodeType());
if (prev != null && !ElementUtils.typeEquals(child.getNodeType(), prev)) {
allSame = false;
}
prev = child.getNodeType();
}
TypeMirror commonNodeSuperType = ElementUtils.getCommonSuperType(context, nodeTypesList);
Types types = context.getEnvironment().getTypeUtils();
TypeMirror factoryType = context.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(context.getType(List.class)), baseType);
CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), listType, "getFactories");
CodeTreeBuilder builder = method.createBuilder();
builder.startReturn();
if (factoryList.size() > 1) {
builder.startStaticCall(context.getType(Arrays.class), "asList");
} else {
builder.startStaticCall(context.getType(Collections.class), "singletonList");
}
for (NodeData child : factoryList) {
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(getAccessorClassName(nodeData)).string(".");
}
builder.string("getInstance()");
builder.end();
}
builder.end();
builder.end();
return method;
}
}