Mercurial > hg > graal-compiler
view graal/com.oracle.graal.nodeinfo.processor/src/com/oracle/graal/nodeinfo/processor/GraphNodeGenerator.java @ 16895:06c15e88d383
added factory method to all Node classes; replaced Node classes instantiation with calls to factory methods; replaced identity tests on Node classes with ' == <node class>.getGenClass()' idiom
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Mon, 18 Aug 2014 14:04:21 +0200 |
parents | 03a4d382c122 |
children | f90dcdbbb75e |
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.graal.nodeinfo.processor; import static com.oracle.truffle.dsl.processor.java.ElementUtils.*; import static java.util.Arrays.*; import static javax.lang.model.element.Modifier.*; import java.util.*; import javax.annotation.processing.*; import javax.lang.model.element.*; import javax.lang.model.type.*; import javax.lang.model.util.*; import com.oracle.graal.nodeinfo.*; import com.oracle.truffle.dsl.processor.java.*; import com.oracle.truffle.dsl.processor.java.model.*; /** * Generates the source code for a Node class. */ public class GraphNodeGenerator { final GraphNodeProcessor env; final TypeElement Input; final TypeElement OptionalInput; final TypeElement Successor; final TypeElement Node; final TypeElement NodeInputList; final TypeElement NodeSuccessorList; public GraphNodeGenerator(GraphNodeProcessor processor) { this.env = processor; this.Input = getType("com.oracle.graal.graph.Node.Input"); this.OptionalInput = getType("com.oracle.graal.graph.Node.OptionalInput"); this.Successor = getType("com.oracle.graal.graph.Node.Successor"); this.Node = getType("com.oracle.graal.graph.Node"); this.NodeInputList = getType("com.oracle.graal.graph.NodeInputList"); this.NodeSuccessorList = getType("com.oracle.graal.graph.NodeSuccessorList"); } /** * Returns a type element given a canonical name. * * @throw {@link NoClassDefFoundError} if a type element does not exist for {@code name} */ public TypeElement getType(String name) { TypeElement typeElement = env.getProcessingEnv().getElementUtils().getTypeElement(name); if (typeElement == null) { throw new NoClassDefFoundError(name); } return typeElement; } public ProcessingEnvironment getProcessingEnv() { return env.getProcessingEnv(); } private static String getGeneratedClassName(TypeElement node) { TypeElement typeElement = node; String newClassName = typeElement.getSimpleName().toString() + "Gen"; Element enclosing = typeElement.getEnclosingElement(); while (enclosing != null) { if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { if (enclosing.getModifiers().contains(Modifier.PRIVATE)) { throw new ElementException(enclosing, "%s %s cannot be private", enclosing.getKind().name().toLowerCase(), enclosing); } newClassName = enclosing.getSimpleName() + "_" + newClassName; } else { assert enclosing.getKind() == ElementKind.PACKAGE; } enclosing = enclosing.getEnclosingElement(); } return newClassName; } public class FieldScanner { /** * @param field * @param isOptional * @param isList * @return true if field scanning should continue */ public boolean scanInputField(VariableElement field, boolean isOptional, boolean isList) { return true; } /** * @param field * @param isList * @return true if field scanning should continue */ public boolean scanSuccessorField(VariableElement field, boolean isList) { return true; } /** * @param field * @return true if field scanning should continue */ public boolean scanDataField(VariableElement field) { return true; } } public boolean isAssignableWithErasure(Element from, Element to) { Types types = env.getProcessingEnv().getTypeUtils(); TypeMirror fromType = types.erasure(from.asType()); TypeMirror toType = types.erasure(to.asType()); return types.isAssignable(fromType, toType); } public void scanFields(TypeElement node, FieldScanner scanner) { TypeElement currentClazz = node; do { for (VariableElement field : ElementFilter.fieldsIn(currentClazz.getEnclosedElements())) { Set<Modifier> modifiers = field.getModifiers(); if (modifiers.contains(STATIC) || modifiers.contains(TRANSIENT)) { continue; } List<? extends AnnotationMirror> annotations = field.getAnnotationMirrors(); boolean isNonOptionalInput = findAnnotationMirror(annotations, Input) != null; boolean isOptionalInput = findAnnotationMirror(annotations, OptionalInput) != null; boolean isSuccessor = findAnnotationMirror(annotations, Successor) != null; if (isNonOptionalInput || isOptionalInput) { if (findAnnotationMirror(annotations, Successor) != null) { throw new ElementException(field, "Field cannot be both input and successor"); } else if (isNonOptionalInput && isOptionalInput) { throw new ElementException(field, "Inputs must be either optional or non-optional"); } else if (isAssignableWithErasure(field, NodeInputList)) { if (!modifiers.contains(FINAL)) { throw new ElementException(field, "Input list field must be final"); } if (modifiers.contains(PUBLIC)) { throw new ElementException(field, "Input list field must not be public"); } if (!scanner.scanInputField(field, isOptionalInput, true)) { return; } } else { if (!isAssignableWithErasure(field, Node) && field.getKind() == ElementKind.INTERFACE) { throw new ElementException(field, "Input field type must be an interface or assignable to Node"); } if (modifiers.contains(FINAL)) { throw new ElementException(field, "Input field must not be final"); } // if (modifiers.contains(PRIVATE) || modifiers.contains(PUBLIC) || modifiers.contains(PROTECTED)) { // throw new ElementException(field, "Input field must be package-private"); // } if (!modifiers.contains(PRIVATE)) { throw new ElementException(field, "Input field must be private"); } if (!scanner.scanInputField(field, isOptionalInput, false)) { return; } } } else if (isSuccessor) { if (isAssignableWithErasure(field, NodeSuccessorList)) { if (!modifiers.contains(FINAL)) { throw new ElementException(field, "Successor list field must be final"); } if (modifiers.contains(PUBLIC)) { throw new ElementException(field, "Successor list field must not be public"); } if (!scanner.scanSuccessorField(field, true)) { return; } } else { if (!isAssignableWithErasure(field, Node)) { throw new ElementException(field, "Successor field must be a Node type"); } if (modifiers.contains(FINAL)) { throw new ElementException(field, "Successor field must not be final"); } // if (modifiers.contains(PRIVATE) || modifiers.contains(PUBLIC) || modifiers.contains(PROTECTED)) { // throw new ElementException(field, "Successor field must be package-private"); // } if (!modifiers.contains(PRIVATE)) { throw new ElementException(field, "Successor field must be private"); } if (!scanner.scanSuccessorField(field, false)) { return; } } } else { if (isAssignableWithErasure(field, Node) && !field.getSimpleName().contentEquals("Null")) { throw new ElementException(field, "Suspicious Node field: " + field); } if (isAssignableWithErasure(field, NodeInputList)) { throw new ElementException(field, "Suspicious NodeInputList field"); } if (isAssignableWithErasure(field, NodeSuccessorList)) { throw new ElementException(field, "Suspicious NodeSuccessorList field"); } if (!scanner.scanDataField(field)) { return; } } } currentClazz = getSuperType(currentClazz); } while (!isObject(getSuperType(currentClazz).asType())); } /** * Determines if two parameter lists contain the * {@linkplain Types#isSameType(TypeMirror, TypeMirror) same} types. */ private boolean parametersMatch(List<? extends VariableElement> p1, List<? extends VariableElement> p2) { if (p1.size() == p2.size()) { for (int i = 0; i < p1.size(); i++) { if (!env.getProcessingEnv().getTypeUtils().isSameType(p1.get(i).asType(), p2.get(i).asType())) { return false; } } return true; } return false; } /** * Searches a type for a method based on a given name and parameter types. */ public ExecutableElement findMethod(TypeElement type, String name, List<? extends VariableElement> parameters) { List<? extends ExecutableElement> methods = ElementFilter.methodsIn(type.getEnclosedElements()); for (ExecutableElement method : methods) { if (method.getSimpleName().toString().equals(name)) { if (parametersMatch(method.getParameters(), parameters)) { return method; } } } return null; } public CodeCompilationUnit process(TypeElement node) { CodeCompilationUnit compilationUnit = new CodeCompilationUnit(); PackageElement packageElement = ElementUtils.findPackageElement(node); String newClassName = getGeneratedClassName(node); CodeTypeElement nodeGenElement = new CodeTypeElement(modifiers(), ElementKind.CLASS, packageElement, newClassName); nodeGenElement.setSuperClass(node.asType()); for (ExecutableElement constructor : ElementFilter.constructorsIn(node.getEnclosedElements())) { if (constructor.getModifiers().contains(PUBLIC)) { throw new ElementException(constructor, "Node class constructor must not be public"); } checkFactoryMethodExists(node, newClassName, constructor); CodeExecutableElement subConstructor = createConstructor(nodeGenElement, constructor); subConstructor.getModifiers().removeAll(Arrays.asList(PUBLIC, PRIVATE, PROTECTED)); nodeGenElement.add(subConstructor); } DeclaredType generatedNode = (DeclaredType) ElementUtils.getType(getProcessingEnv(), GeneratedNode.class); CodeAnnotationMirror generatedByMirror = new CodeAnnotationMirror(generatedNode); generatedByMirror.setElementValue(generatedByMirror.findExecutableElement("value"), new CodeAnnotationValue(node.asType())); nodeGenElement.getAnnotationMirrors().add(generatedByMirror); nodeGenElement.add(createIsLeafNodeMethod(node)); compilationUnit.add(nodeGenElement); return compilationUnit; } /** * Checks that a public static factory method named {@code "create"} exists in {@code node} * whose signature matches that of a given constructor. * * @throws ElementException if the check fails */ private void checkFactoryMethodExists(TypeElement node, String newClassName, ExecutableElement constructor) { ExecutableElement create = findMethod(node, "create", constructor.getParameters()); if (create == null) { Formatter f = new Formatter(); f.format("public static %s create(", node.getSimpleName()); String sep = ""; Formatter callArgs = new Formatter(); for (VariableElement v : constructor.getParameters()) { f.format("%s%s %s", sep, ElementUtils.getSimpleName(v.asType()), v.getSimpleName()); callArgs.format("%s%s", sep, v.getSimpleName()); sep = ", "; } f.format(") { return new %s(%s); }", newClassName, callArgs); throw new ElementException(constructor, "Missing Node class factory method '%s'", f); } if (!create.getModifiers().containsAll(asList(PUBLIC, STATIC))) { throw new ElementException(constructor, "Node class factory method must be public and static"); } } private CodeExecutableElement createConstructor(TypeElement type, ExecutableElement element) { CodeExecutableElement executable = CodeExecutableElement.clone(getProcessingEnv(), element); // to create a constructor we have to set the return type to null.(TODO needs fix) executable.setReturnType(null); // we have to set the name manually otherwise <init> is inferred (TODO needs fix) executable.setSimpleName(CodeNames.of(type.getSimpleName().toString())); CodeTreeBuilder b = executable.createBuilder(); b.startStatement().startSuperCall(); for (VariableElement v : element.getParameters()) { b.string(v.getSimpleName().toString()); } b.end().end(); return executable; } public ExecutableElement createIsLeafNodeMethod(TypeElement node) { CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), ElementUtils.getType(getProcessingEnv(), boolean.class), "isLeafNode"); boolean[] isLeafNode = {true}; scanFields(node, new FieldScanner() { @Override public boolean scanInputField(VariableElement field, boolean isOptional, boolean isList) { isLeafNode[0] = false; return false; } @Override public boolean scanSuccessorField(VariableElement field, boolean isList) { isLeafNode[0] = false; return false; } }); CodeTreeBuilder builder = method.createBuilder(); builder.startReturn().string(String.valueOf(isLeafNode[0])).end(); return method; } }