# HG changeset patch # User Gilles Duboscq # Date 1429793332 -7200 # Node ID 391f94d4d23fc42ade7b32c977a18d1b835d05b9 # Parent 71e05c3a1e122ad11f5bc4fd12856889c514ad85 Move MatchProcessor and HotSpotVMConfigPorcessor to their own projects diff -r 71e05c3a1e12 -r 391f94d4d23f graal/com.oracle.graal.compiler.match.processor/src/META-INF/services/javax.annotation.processing.Processor --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.match.processor/src/META-INF/services/javax.annotation.processing.Processor Thu Apr 23 14:48:52 2015 +0200 @@ -0,0 +1,1 @@ +com.oracle.graal.compiler.match.processor.MatchProcessor diff -r 71e05c3a1e12 -r 391f94d4d23f graal/com.oracle.graal.compiler.match.processor/src/com/oracle/graal/compiler/match/processor/MatchProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.match.processor/src/com/oracle/graal/compiler/match/processor/MatchProcessor.java Thu Apr 23 14:48:52 2015 +0200 @@ -0,0 +1,1104 @@ +/* + * 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.compiler.match.processor; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; +import javax.tools.Diagnostic.Kind; +import javax.tools.*; + +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.gen.*; +import com.oracle.graal.compiler.match.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; + +/** + * Processes classes annotated with {@link MatchRule}. A {@link MatchStatementSet} service is + * generated for each top level class containing at least one such field. These service objects can + * be retrieved as follows: + * + *
+ *     Iterable sl = Services.load(MatchStatementSet.class);
+ *     for (MatchStatementSet rules : sl) {
+ *         ...
+ *     }
+ * 
+ */ +@SupportedAnnotationTypes({"com.oracle.graal.compiler.match.MatchRule", "com.oracle.graal.compiler.match.MatchRules", "com.oracle.graal.compiler.match.MatchableNode", + "com.oracle.graal.compiler.match.MatchableNodes"}) +public class MatchProcessor extends AbstractProcessor { + + public MatchProcessor() { + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + private final Set processedMatchRule = new HashSet<>(); + private final Set processedMatchableNode = new HashSet<>(); + + private static class RuleParseError extends RuntimeException { + private static final long serialVersionUID = 6456128283609257490L; + + RuleParseError(String format, Object... args) { + super(String.format(format, args)); + } + } + + private static final Pattern tokenizer = Pattern.compile("\\s*([()=]|[A-Za-z][A-Za-z0-9]*)\\s*"); + + private class RuleParser { + private ArrayList capturedTypes = new ArrayList<>(); + + private ArrayList capturedNames = new ArrayList<>(); + + private final String[] tokens; + + private int current; + + private MatchDescriptor matchDescriptor; + + private final Set originatingElements = new HashSet<>(); + + private Set requiredPackages = new HashSet<>(); + + RuleParser(String rule) { + Matcher m = tokenizer.matcher(rule); + List list = new ArrayList<>(); + int end = 0; + while (m.lookingAt()) { + list.add(m.group(1)); + end = m.end(); + m.region(m.end(), m.regionEnd()); + } + if (end != m.regionEnd()) { + throw new RuleParseError("Unexpected tokens :" + rule.substring(m.end(), m.regionEnd())); + } + tokens = list.toArray(new String[0]); + + matchDescriptor = parseExpression(); + if (!done()) { + throw new RuleParseError("didn't consume all tokens"); + } + capturedNames.add(0, "root"); + capturedTypes.add(0, matchDescriptor.nodeType); + } + + String next() { + return tokens[current++]; + } + + String peek(String name) { + if (current >= tokens.length) { + if (name == null) { + throw new RuleParseError("Out of tokens"); + } + throw new RuleParseError("Out of tokens looking for %s", name); + } + return tokens[current]; + } + + boolean done() { + return current == tokens.length; + } + + private MatchDescriptor parseExpression() { + if (peek("(").equals("(")) { + next(); + MatchDescriptor descriptor = parseType(true); + for (int n = 0; n < descriptor.nodeType.inputs.length; n++) { + if (peek("(").equals("(")) { + descriptor.inputs[n] = parseExpression(); + } else { + descriptor.inputs[n] = parseType(false); + } + } + for (int n = 0; n < descriptor.nodeType.inputs.length; n++) { + if (descriptor.inputs[n] == null) { + throw new RuleParseError("not enough inputs for " + descriptor.name); + } + } + if (peek(")").equals(")")) { + next(); + return descriptor; + } + throw new RuleParseError("Too many arguments to " + descriptor.nodeType.nodeClass); + } + throw new RuleParseError("Extra tokens following match pattern: " + peek(null)); + } + + private MatchDescriptor parseType(boolean forExpression) { + TypeDescriptor type = null; + String name = null; + if (Character.isUpperCase(peek("node type or name").charAt(0))) { + String token = next(); + type = knownTypes.get(token); + if (type == null) { + throw new RuleParseError("Unknown node type: " + token); + } + if (peek("=").equals("=")) { + next(); + name = next(); + } + originatingElements.addAll(type.originatingElements); + requiredPackages.add(type.nodePackage); + } else if (Character.isLowerCase(peek("name").charAt(0))) { + name = next(); + type = valueType; + } else { + throw new RuleParseError("Unexpected token \"%s\" when looking for name or node type", peek(null)); + } + if (name != null) { + if (!capturedNames.contains(name)) { + capturedNames.add(name); + capturedTypes.add(type); + } else { + int index = capturedNames.indexOf(name); + if (capturedTypes.get(index) != type) { + throw new RuleParseError("Captured node \"%s\" has differing types", name); + } + } + } + return new MatchDescriptor(type, name, forExpression); + } + + List generateVariants() { + return matchDescriptor.generateVariants(); + } + + /** + * Recursively accumulate any required Position declarations. + */ + void generatePositionDeclarations(Set declarations) { + matchDescriptor.generatePositionDeclarations(declarations); + } + + /** + * + * @return the list of node types which are captured by name + */ + public ArrayList capturedTypes() { + return capturedTypes; + } + + public ArrayList capturedNames() { + return capturedNames; + } + } + + /** + * Set to true to enable logging to a local file during annotation processing. There's no normal + * channel for any debug messages and debugging annotation processors requires some special + * setup. + */ + private static final boolean DEBUG = false; + + private PrintWriter log; + + /** + * Logging facility for debugging the annotation processor. + */ + + private PrintWriter getLog() { + if (log == null) { + try { + // Create the log file within the generated source directory so it's easy to find. + // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly + // on the mac. + FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); + log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); + } catch (IOException e) { + // Do nothing + } + } + return log; + } + + private void logMessage(String format, Object... args) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + bw.printf(format, args); + bw.flush(); + } + } + + private void logException(Throwable t) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + t.printStackTrace(bw); + bw.flush(); + } + } + + /** + * Bugs in an annotation processor can cause silent failure so try to report any exception + * throws as errors. + */ + private void reportExceptionThrow(Element element, Throwable t) { + if (element != null) { + logMessage("throw for %s:\n", element); + } + logException(t); + errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); + } + + static class TypeDescriptor { + final TypeMirror mirror; + + /** + * The name uses in match expressions to refer to this type. + */ + final String shortName; + + /** + * The simple name of the {@link ValueNode} class represented by this type. + */ + final String nodeClass; + + /** + * The package of {@link ValueNode} class represented by this type. + */ + final String nodePackage; + + /** + * The matchable inputs of the node. + */ + final String[] inputs; + + /** + * Should swapped variants of this match be generated. The user of the match is expected to + * compensate for any ordering differences in compare which are commutative but require + * reinterpreting the condition in that case. + */ + final boolean commutative; + + /** + * Can multiple users of this node subsume it. Constants can be swallowed into a match even + * if there are multiple users. + */ + final boolean shareable; + + final Set originatingElements = new HashSet<>(); + + TypeDescriptor(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, String[] inputs, boolean commutative, boolean shareable) { + this.mirror = mirror; + this.shortName = shortName; + this.nodeClass = nodeClass; + this.nodePackage = nodePackage; + this.inputs = inputs; + this.commutative = commutative; + this.shareable = shareable; + assert !commutative || inputs.length == 2; + } + } + + /** + * The types which are know for purpose of parsing MatchRule expressions. + */ + Map knownTypes = new HashMap<>(); + + /** + * The mapping between elements with MatchRules and the wrapper class used invoke the code + * generation after the match. + */ + private Map invokers = new LinkedHashMap<>(); + + private TypeDescriptor valueType; + + private TypeMirror matchRulesTypeMirror; + + private TypeMirror matchRuleTypeMirror; + + private TypeMirror matchableNodeTypeMirror; + + private TypeMirror matchableNodesTypeMirror; + + private void declareType(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, String[] inputs, boolean commutative, boolean shareable, Element element) { + TypeDescriptor descriptor = new TypeDescriptor(mirror, shortName, nodeClass, nodePackage, inputs, commutative, shareable); + descriptor.originatingElements.add(element); + knownTypes.put(shortName, descriptor); + } + + private String findPackage(Element type) { + PackageElement p = processingEnv.getElementUtils().getPackageOf(type); + if (p != null) { + return p.getQualifiedName().toString(); + } + throw new GraalInternalError("can't find package for %s", type); + } + + class MatchDescriptor { + TypeDescriptor nodeType; + String name; + MatchDescriptor[] inputs; + + MatchDescriptor(TypeDescriptor nodeType, String name, boolean forExpression) { + this.nodeType = nodeType; + this.name = name; + if (forExpression) { + this.inputs = new MatchDescriptor[nodeType.inputs.length]; + } else { + this.inputs = new MatchDescriptor[0]; + } + } + + public void generatePositionDeclarations(Set declarations) { + if (inputs.length == 0) { + return; + } + declarations.add(generatePositionDeclaration()); + for (MatchDescriptor desc : inputs) { + desc.generatePositionDeclarations(declarations); + } + } + + List recurseVariants(int index) { + if (inputs.length == 0) { + return new ArrayList<>(); + } + List currentVariants = inputs[index].generateVariants(); + if (index == inputs.length - 1) { + return currentVariants; + } + List subVariants = recurseVariants(index + 1); + List result = new ArrayList<>(); + for (String current : currentVariants) { + for (String sub : subVariants) { + result.add(current + ", " + sub); + if (nodeType.commutative) { + result.add(sub + ", " + current); + } + } + } + return result; + } + + /** + * Recursively generate all the variants of this rule pattern. Currently that just means to + * swap the inputs for commutative rules, producing all possible permutations. + * + * @return a list of Strings which will construct pattern matchers for this rule. + */ + List generateVariants() { + String prefix = formatPrefix(); + String suffix = formatSuffix(); + ArrayList variants = new ArrayList<>(); + if (inputs.length > 0) { + for (String var : recurseVariants(0)) { + variants.add(prefix + ", " + var + suffix); + } + } else { + assert inputs.length == 0; + variants.add(prefix + suffix); + } + + return variants; + } + + private String formatPrefix() { + if (nodeType == valueType) { + return String.format("new MatchPattern(%s, false", name != null ? ("\"" + name + "\"") : "null"); + } else { + return String.format("new MatchPattern(%s.class, %s", nodeType.nodeClass, name != null ? ("\"" + name + "\"") : "null"); + } + } + + private String formatSuffix() { + if (nodeType != null) { + if (inputs.length != nodeType.inputs.length) { + return ", true)"; + } else { + if (nodeType.inputs.length > 0) { + return ", " + nodeType.nodeClass + "_positions, " + !nodeType.shareable + ")"; + } + if (nodeType.shareable) { + return ", false)"; + } + } + } + return ")"; + } + + String generatePositionDeclaration() { + return String.format("Position[] %s_positions = MatchRuleRegistry.findPositions(%s.TYPE, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass, + String.join("\", \"", nodeType.inputs)); + } + } + + /** + * Strip the package off a class name leaving the full class name including any outer classes. + */ + private String fullClassName(Element element) { + assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE : element; + String pkg = findPackage(element); + return ((TypeElement) element).getQualifiedName().toString().substring(pkg.length() + 1); + } + + private void createFiles(MatchRuleDescriptor info) { + String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString(); + Name topDeclaringClass = info.topDeclaringType.getSimpleName(); + + String matchStatementClassName = topDeclaringClass + "_" + MatchStatementSet.class.getSimpleName(); + Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]); + + Types typeUtils = typeUtils(); + Filer filer = processingEnv.getFiler(); + try (PrintWriter out = createSourceFile(pkg, matchStatementClassName, filer, originatingElements)) { + + out.println("// CheckStyle: stop header check"); + out.println("// GENERATED CONTENT - DO NOT EDIT"); + out.println("// Source: " + topDeclaringClass + ".java"); + out.println("package " + pkg + ";"); + out.println(""); + out.println("import java.util.*;"); + out.println("import " + MatchStatementSet.class.getPackage().getName() + ".*;"); + out.println("import " + NodeLIRBuilder.class.getName() + ";"); + out.println("import " + Position.class.getName() + ";"); + for (String p : info.requiredPackages) { + out.println("import " + p + ".*;"); + } + out.println(""); + + out.println("public class " + matchStatementClassName + " implements " + MatchStatementSet.class.getSimpleName() + " {"); + + out.println(); + + // Generate declarations for the wrapper class to invoke the code generation methods. + for (MethodInvokerItem invoker : invokers.values()) { + StringBuilder args = new StringBuilder(); + StringBuilder types = new StringBuilder(); + int count = invoker.fields.size(); + int index = 0; + for (VariableElement arg : invoker.fields) { + args.append('"'); + args.append(arg.getSimpleName()); + args.append('"'); + types.append(String.format("(%s) args[%s]", fullClassName(typeUtils.asElement(arg.asType())), index++)); + if (count-- > 1) { + args.append(", "); + types.append(", "); + } + } + out.printf(" private static final String[] %s = new String[] {%s};\n", invoker.argumentsListName(), args); + out.printf(" private static final class %s implements MatchGenerator {\n", invoker.wrapperClass()); + out.printf(" static MatchGenerator instance = new %s();\n", invoker.wrapperClass()); + out.printf(" public ComplexMatchResult match(NodeLIRBuilder builder, Object...args) {\n"); + out.printf(" return ((%s) builder).%s(%s);\n", invoker.nodeLIRBuilderClass, invoker.methodName, types); + out.printf(" }\n"); + out.printf(" public String getName() {\n"); + out.printf(" return \"%s\";\n", invoker.methodName); + out.printf(" }\n"); + out.printf(" }\n"); + out.println(); + + } + + String desc = MatchStatement.class.getSimpleName(); + + out.println(" public Class forClass() {"); + out.println(" return " + topDeclaringClass + ".class;"); + out.println(" }"); + out.println(); + out.println(" @Override"); + out.println(" public List<" + desc + "> statements() {"); + out.println(" // Checkstyle: stop "); + + for (String positionDeclaration : info.positionDeclarations) { + out.println(" " + positionDeclaration); + } + out.println(); + + out.println(" List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList("); + + int i = 0; + for (MatchRuleItem matchRule : info.matchRules) { + String comma = i == info.matchRules.size() - 1 ? "" : ","; + out.printf(" %s%s\n", matchRule.ruleBuilder(), comma); + i++; + } + out.println(" ));"); + out.println(" // Checkstyle: resume"); + out.println(" return statements;"); + out.println(" }"); + + out.println(); + + out.println("}"); + } + + try { + createProviderFile(pkg, matchStatementClassName, originatingElements); + } catch (IOException e) { + reportExceptionThrow(info.topDeclaringType, e); + } + } + + private void createProviderFile(String pkg, String providerClassName, Element... originatingElements) throws IOException { + String filename = "META-INF/providers/" + pkg + "." + providerClassName; + FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements); + PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8")); + writer.println(MatchStatementSet.class.getName()); + writer.close(); + } + + protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { + try { + // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle + JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements); + return new PrintWriter(sourceFile.openWriter()) { + + @Override + public void println() { + print("\n"); + } + }; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Used to generate the MatchStatement constructor invocation. + */ + static class MatchRuleItem { + private final String matchPattern; + private final MethodInvokerItem invoker; + + public MatchRuleItem(String matchPattern, MethodInvokerItem invoker) { + this.matchPattern = matchPattern; + this.invoker = invoker; + } + + /** + * @return a string which will construct the MatchStatement instance to match this pattern. + */ + public String ruleBuilder() { + return String.format("new MatchStatement(\"%s\", %s, %s.instance, %s)", invoker.methodName, matchPattern, invoker.wrapperClass(), invoker.argumentsListName()); + } + } + + /** + * Used to generate the wrapper class to invoke the code generation method. + */ + static class MethodInvokerItem { + final String methodName; + final String nodeLIRBuilderClass; + final ExecutableElement method; + final List fields; + + MethodInvokerItem(String methodName, String nodeLIRBuilderClass, ExecutableElement method, List fields) { + this.methodName = methodName; + this.nodeLIRBuilderClass = nodeLIRBuilderClass; + this.method = method; + this.fields = fields; + } + + String wrapperClass() { + return "MatchGenerator_" + methodName; + } + + String argumentsListName() { + return methodName + "_arguments"; + } + } + + static class MatchRuleDescriptor { + + final TypeElement topDeclaringType; + final List matchRules = new ArrayList<>(); + private final Set originatingElements = new HashSet<>(); + public Set positionDeclarations = new LinkedHashSet<>(); + + /** + * The set of packages which must be imported to refer the classes mention in matchRules. + */ + Set requiredPackages = new HashSet<>(); + + public MatchRuleDescriptor(TypeElement topDeclaringType) { + this.topDeclaringType = topDeclaringType; + } + } + + private static TypeElement topDeclaringType(Element element) { + Element enclosing = element.getEnclosingElement(); + if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) { + assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE; + return (TypeElement) element; + } + return topDeclaringType(enclosing); + } + + private AnnotationMirror findAnnotationMirror(Element element, TypeMirror typeMirror) { + for (AnnotationMirror mirror : element.getAnnotationMirrors()) { + if (typeUtils().isSameType(mirror.getAnnotationType(), typeMirror)) { + return mirror; + } + } + return null; + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return true; + } + + logMessage("Starting round %s\n", roundEnv); + matchRulesTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchRules.class.getCanonicalName()).asType(); + matchRuleTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchRule.class.getCanonicalName()).asType(); + + matchableNodeTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchableNode.class.getCanonicalName()).asType(); + matchableNodesTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchableNodes.class.getCanonicalName()).asType(); + + Element currentElement = null; + try { + for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNode.class)) { + logMessage("%s\n", element); + processMatchableNode(element); + } + for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNodes.class)) { + logMessage("%s\n", element); + processMatchableNode(element); + } + // Define a TypeDescriptor for the generic node but don't enter it into the nodeTypes + // table since it shouldn't be mentioned in match rules. + TypeMirror valueTypeMirror = processingEnv.getElementUtils().getTypeElement(ValueNode.class.getName()).asType(); + valueType = new TypeDescriptor(valueTypeMirror, "Value", ValueNode.class.getSimpleName(), ValueNode.class.getPackage().getName(), new String[0], false, false); + + Map map = new LinkedHashMap<>(); + + for (Element element : roundEnv.getElementsAnnotatedWith(MatchRule.class)) { + currentElement = element; + processMatchRule(map, element, findAnnotationMirror(element, matchRuleTypeMirror)); + } + for (Element element : roundEnv.getElementsAnnotatedWith(MatchRules.class)) { + currentElement = element; + processMatchRule(map, element, findAnnotationMirror(element, matchRulesTypeMirror)); + } + + currentElement = null; + for (MatchRuleDescriptor info : map.values()) { + createFiles(info); + } + + } catch (Throwable t) { + reportExceptionThrow(currentElement, t); + } + + return true; + } + + /** + * Build up the type table to be used during parsing of the MatchRule. + */ + private void processMatchableNode(Element element) { + if (!processedMatchableNode.contains(element)) { + try { + processedMatchableNode.add(element); + + AnnotationMirror mirror = findAnnotationMirror(element, matchableNodesTypeMirror); + if (mirror == null) { + mirror = findAnnotationMirror(element, matchableNodeTypeMirror); + } + if (mirror == null) { + return; + } + TypeElement topDeclaringType = topDeclaringType(element); + List mirrors = null; + if (typeUtils().isSameType(mirror.getAnnotationType(), matchableNodesTypeMirror)) { + // Unpack the mirrors for a repeatable annotation + mirrors = getAnnotationValueList(AnnotationMirror.class, mirror, "value"); + } + int i = 0; + for (MatchableNode matchableNode : element.getAnnotationsByType(MatchableNode.class)) { + processMatchableNode(element, topDeclaringType, matchableNode, mirrors != null ? mirrors.get(i++) : mirror); + } + } catch (Throwable t) { + reportExceptionThrow(element, t); + } + } + } + + private void processMatchableNode(Element element, TypeElement topDeclaringType, MatchableNode matchable, AnnotationMirror mirror) throws GraalInternalError { + logMessage("processMatchableNode %s %s %s\n", topDeclaringType, element, matchable); + String nodeClass; + String nodePackage; + TypeMirror nodeClassMirror = null; + try { + matchable.nodeClass(); + } catch (MirroredTypeException e) { + nodeClassMirror = e.getTypeMirror(); + } + if (nodeClassMirror == null) { + throw new GraalInternalError("Can't get mirror for node class %s", element); + } + if (nodeClassMirror.toString().equals(MatchableNode.class.getName())) { + nodeClass = topDeclaringType.getQualifiedName().toString(); + } else { + nodeClass = nodeClassMirror.toString(); + } + TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(nodeClass); + if (typeElement == null) { + errorMessage(element, mirror, "Class \"%s\" cannot be resolved to a type", nodeClass); + return; + } + nodePackage = findPackage(typeElement); + assert nodeClass.startsWith(nodePackage); + nodeClass = nodeClass.substring(nodePackage.length() + 1); + assert nodeClass.endsWith("Node"); + String shortName = nodeClass.substring(0, nodeClass.length() - 4); + + Types typeUtils = processingEnv.getTypeUtils(); + TypeElement nodeClassElement = (TypeElement) typeUtils.asElement(nodeClassMirror); + for (String input : matchable.inputs()) { + boolean ok = false; + TypeElement current = nodeClassElement; + while (!ok && current != null) { + for (Element fieldElement : ElementFilter.fieldsIn(current.getEnclosedElements())) { + if (fieldElement.getSimpleName().toString().equals(input)) { + ok = true; + break; + } + } + TypeMirror theSuper = current.getSuperclass(); + current = (TypeElement) typeUtils.asElement(theSuper); + } + if (!ok) { + errorMessage(element, mirror, "Input named \"%s\" doesn't exist in %s", input, nodeClassElement.getSimpleName()); + } + } + + declareType(nodeClassMirror, shortName, nodeClass, nodePackage, matchable.inputs(), matchable.commutative(), matchable.shareable(), element); + } + + private void processMatchRule(Map map, Element element, AnnotationMirror mirror) { + if (!processedMatchRule.contains(element)) { + try { + processedMatchRule.add(element); + + // The annotation element type should ensure this is true. + assert element instanceof ExecutableElement; + + findMatchableNodes(element); + + TypeElement topDeclaringType = topDeclaringType(element); + MatchRuleDescriptor info = map.get(topDeclaringType); + if (info == null) { + info = new MatchRuleDescriptor(topDeclaringType); + map.put(topDeclaringType, info); + } + List mirrors = null; + if (typeUtils().isSameType(mirror.getAnnotationType(), matchRulesTypeMirror)) { + // Unpack the mirrors for a repeatable annotation + mirrors = getAnnotationValueList(AnnotationMirror.class, mirror, "value"); + } + int i = 0; + for (MatchRule matchRule : element.getAnnotationsByType(MatchRule.class)) { + processMethodMatchRule((ExecutableElement) element, info, matchRule, mirrors != null ? mirrors.get(i++) : mirror); + } + } catch (Throwable t) { + reportExceptionThrow(element, t); + } + } + } + + /** + * Search the super types of element for MatchableNode definitions. Any superclass or super + * interface can contain definitions of matchable nodes. + * + * @param element + */ + private void findMatchableNodes(Element element) { + processMatchableNode(element); + Element enclosing = element.getEnclosingElement(); + while (enclosing != null) { + if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { + TypeElement current = (TypeElement) enclosing; + while (current != null) { + processMatchableNode(current); + for (TypeMirror intf : current.getInterfaces()) { + Element interfaceElement = typeUtils().asElement(intf); + processMatchableNode(interfaceElement); + // Recurse + findMatchableNodes(interfaceElement); + } + TypeMirror theSuper = current.getSuperclass(); + current = (TypeElement) typeUtils().asElement(theSuper); + } + } + enclosing = enclosing.getEnclosingElement(); + } + } + + private Types typeUtils() { + return processingEnv.getTypeUtils(); + } + + private void processMethodMatchRule(ExecutableElement method, MatchRuleDescriptor info, MatchRule matchRule, AnnotationMirror mirror) { + logMessage("processMethodMatchRule %s %s\n", method, mirror); + + Types typeUtils = typeUtils(); + + if (!method.getModifiers().contains(Modifier.PUBLIC)) { + errorMessage(method, "MatchRule method %s must be public", method.getSimpleName()); + return; + } + if (method.getModifiers().contains(Modifier.STATIC)) { + errorMessage(method, "MatchRule method %s must be non-static", method.getSimpleName()); + return; + } + + try { + TypeMirror returnType = method.getReturnType(); + if (!typeUtils.isSameType(returnType, processingEnv.getElementUtils().getTypeElement(ComplexMatchResult.class.getName()).asType())) { + errorMessage(method, "MatchRule method return type must be %s", ComplexMatchResult.class.getName()); + return; + } + + String rule = matchRule.value(); + RuleParser parser = new RuleParser(rule); + ArrayList expectedTypes = parser.capturedTypes(); + ArrayList expectedNames = parser.capturedNames(); + List actualParameters = method.getParameters(); + if (expectedTypes.size() + 1 < actualParameters.size()) { + errorMessage(method, "Too many arguments for match method %s != %s", expectedTypes.size() + 1, actualParameters.size()); + return; + } + + // Walk through the parameters to the method and see if they exist in the match rule. + // The order doesn't matter but only names mentioned in the rule can be used and they + // must be assignment compatible. + for (VariableElement parameter : actualParameters) { + String name = parameter.getSimpleName().toString(); + int nameIndex = expectedNames.indexOf(name); + if (nameIndex == -1) { + errorMessage(method, "Argument \"%s\" isn't captured in the match rule", name); + return; + } + TypeMirror type = parameter.asType(); + if (!typeUtils.isAssignable(expectedTypes.get(nameIndex).mirror, type)) { + errorMessage(method, "Captured value \"%s\" of type %s is not assignable to argument of type %s", name, expectedTypes.get(nameIndex).mirror, type); + return; + } + } + + String methodName = method.getSimpleName().toString(); + MethodInvokerItem invoker = invokers.get(methodName); + if (invoker == null) { + invoker = new MethodInvokerItem(methodName, topDeclaringType(method).getSimpleName().toString(), method, actualParameters); + invokers.put(methodName, invoker); + } else if (invoker.method != method) { + // This could be supported but it's easier if they are unique since the names + // are used in log output and snippet counters. + errorMessage(method, "Use unique method names for match methods."); + return; + } + + Element enclosing = method.getEnclosingElement(); + String declaringClass = ""; + String separator = ""; + Set originatingElementsList = info.originatingElements; + originatingElementsList.add(method); + while (enclosing != null) { + if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { + if (enclosing.getModifiers().contains(Modifier.PRIVATE)) { + errorMessage(method, "MatchRule cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing); + return; + } + originatingElementsList.add(enclosing); + declaringClass = enclosing.getSimpleName() + separator + declaringClass; + separator = "."; + } else { + assert enclosing.getKind() == ElementKind.PACKAGE; + } + enclosing = enclosing.getEnclosingElement(); + } + + originatingElementsList.addAll(parser.originatingElements); + info.requiredPackages.addAll(parser.requiredPackages); + + // Accumulate any position declarations. + parser.generatePositionDeclarations(info.positionDeclarations); + + List matches = parser.generateVariants(); + for (String match : matches) { + info.matchRules.add(new MatchRuleItem(match, invoker)); + } + } catch (RuleParseError e) { + errorMessage(method, mirror, e.getMessage()); + } + } + + private void errorMessage(Element element, String format, Object... args) { + processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); + } + + private void errorMessage(Element element, AnnotationMirror mirror, String format, Object... args) { + processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element, mirror); + } + + // TODO borrowed from com.oracle.truffle.dsl.processor.Utils + @SuppressWarnings("unchecked") + private static List getAnnotationValueList(Class expectedListType, AnnotationMirror mirror, String name) { + List values = getAnnotationValue(List.class, mirror, name); + List result = new ArrayList<>(); + + if (values != null) { + for (AnnotationValue value : values) { + T annotationValue = resolveAnnotationValue(expectedListType, value); + if (annotationValue != null) { + result.add(annotationValue); + } + } + } + return result; + } + + private static T getAnnotationValue(Class expectedType, AnnotationMirror mirror, String name) { + return resolveAnnotationValue(expectedType, getAnnotationValue(mirror, name)); + } + + @SuppressWarnings({"unchecked"}) + private static T resolveAnnotationValue(Class expectedType, AnnotationValue value) { + if (value == null) { + return null; + } + + Object unboxedValue = value.accept(new AnnotationValueVisitorImpl(), null); + if (unboxedValue != null) { + if (expectedType == TypeMirror.class && unboxedValue instanceof String) { + return null; + } + if (!expectedType.isAssignableFrom(unboxedValue.getClass())) { + throw new ClassCastException(unboxedValue.getClass().getName() + " not assignable from " + expectedType.getName()); + } + } + return (T) unboxedValue; + } + + private static AnnotationValue getAnnotationValue(AnnotationMirror mirror, String name) { + ExecutableElement valueMethod = null; + for (ExecutableElement method : ElementFilter.methodsIn(mirror.getAnnotationType().asElement().getEnclosedElements())) { + if (method.getSimpleName().toString().equals(name)) { + valueMethod = method; + break; + } + } + + if (valueMethod == null) { + return null; + } + + AnnotationValue value = mirror.getElementValues().get(valueMethod); + if (value == null) { + value = valueMethod.getDefaultValue(); + } + + return value; + } + + private static class AnnotationValueVisitorImpl extends AbstractAnnotationValueVisitor7 { + + @Override + public Object visitBoolean(boolean b, Void p) { + return Boolean.valueOf(b); + } + + @Override + public Object visitByte(byte b, Void p) { + return Byte.valueOf(b); + } + + @Override + public Object visitChar(char c, Void p) { + return c; + } + + @Override + public Object visitDouble(double d, Void p) { + return d; + } + + @Override + public Object visitFloat(float f, Void p) { + return f; + } + + @Override + public Object visitInt(int i, Void p) { + return i; + } + + @Override + public Object visitLong(long i, Void p) { + return i; + } + + @Override + public Object visitShort(short s, Void p) { + return s; + } + + @Override + public Object visitString(String s, Void p) { + return s; + } + + @Override + public Object visitType(TypeMirror t, Void p) { + return t; + } + + @Override + public Object visitEnumConstant(VariableElement c, Void p) { + return c; + } + + @Override + public Object visitAnnotation(AnnotationMirror a, Void p) { + return a; + } + + @Override + public Object visitArray(List vals, Void p) { + return vals; + } + + } +} diff -r 71e05c3a1e12 -r 391f94d4d23f graal/com.oracle.graal.compiler/src/META-INF/services/javax.annotation.processing.Processor --- a/graal/com.oracle.graal.compiler/src/META-INF/services/javax.annotation.processing.Processor Tue Apr 21 17:32:06 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.oracle.graal.compiler.match.MatchProcessor diff -r 71e05c3a1e12 -r 391f94d4d23f graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java Tue Apr 21 17:32:06 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1103 +0,0 @@ -/* - * 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.compiler.match; - -import java.io.*; -import java.util.*; -import java.util.regex.*; - -import javax.annotation.processing.*; -import javax.lang.model.*; -import javax.lang.model.element.*; -import javax.lang.model.type.*; -import javax.lang.model.util.*; -import javax.tools.Diagnostic.Kind; -import javax.tools.*; - -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.compiler.gen.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; - -/** - * Processes classes annotated with {@link MatchRule}. A {@link MatchStatementSet} service is - * generated for each top level class containing at least one such field. These service objects can - * be retrieved as follows: - * - *
- *     Iterable sl = Services.load(MatchStatementSet.class);
- *     for (MatchStatementSet rules : sl) {
- *         ...
- *     }
- * 
- */ -@SupportedAnnotationTypes({"com.oracle.graal.compiler.match.MatchRule", "com.oracle.graal.compiler.match.MatchRules", "com.oracle.graal.compiler.match.MatchableNode", - "com.oracle.graal.compiler.match.MatchableNodes"}) -public class MatchProcessor extends AbstractProcessor { - - public MatchProcessor() { - } - - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - private final Set processedMatchRule = new HashSet<>(); - private final Set processedMatchableNode = new HashSet<>(); - - private static class RuleParseError extends RuntimeException { - private static final long serialVersionUID = 6456128283609257490L; - - RuleParseError(String format, Object... args) { - super(String.format(format, args)); - } - } - - private static final Pattern tokenizer = Pattern.compile("\\s*([()=]|[A-Za-z][A-Za-z0-9]*)\\s*"); - - private class RuleParser { - private ArrayList capturedTypes = new ArrayList<>(); - - private ArrayList capturedNames = new ArrayList<>(); - - private final String[] tokens; - - private int current; - - private MatchDescriptor matchDescriptor; - - private final Set originatingElements = new HashSet<>(); - - private Set requiredPackages = new HashSet<>(); - - RuleParser(String rule) { - Matcher m = tokenizer.matcher(rule); - List list = new ArrayList<>(); - int end = 0; - while (m.lookingAt()) { - list.add(m.group(1)); - end = m.end(); - m.region(m.end(), m.regionEnd()); - } - if (end != m.regionEnd()) { - throw new RuleParseError("Unexpected tokens :" + rule.substring(m.end(), m.regionEnd())); - } - tokens = list.toArray(new String[0]); - - matchDescriptor = parseExpression(); - if (!done()) { - throw new RuleParseError("didn't consume all tokens"); - } - capturedNames.add(0, "root"); - capturedTypes.add(0, matchDescriptor.nodeType); - } - - String next() { - return tokens[current++]; - } - - String peek(String name) { - if (current >= tokens.length) { - if (name == null) { - throw new RuleParseError("Out of tokens"); - } - throw new RuleParseError("Out of tokens looking for %s", name); - } - return tokens[current]; - } - - boolean done() { - return current == tokens.length; - } - - private MatchDescriptor parseExpression() { - if (peek("(").equals("(")) { - next(); - MatchDescriptor descriptor = parseType(true); - for (int n = 0; n < descriptor.nodeType.inputs.length; n++) { - if (peek("(").equals("(")) { - descriptor.inputs[n] = parseExpression(); - } else { - descriptor.inputs[n] = parseType(false); - } - } - for (int n = 0; n < descriptor.nodeType.inputs.length; n++) { - if (descriptor.inputs[n] == null) { - throw new RuleParseError("not enough inputs for " + descriptor.name); - } - } - if (peek(")").equals(")")) { - next(); - return descriptor; - } - throw new RuleParseError("Too many arguments to " + descriptor.nodeType.nodeClass); - } - throw new RuleParseError("Extra tokens following match pattern: " + peek(null)); - } - - private MatchDescriptor parseType(boolean forExpression) { - TypeDescriptor type = null; - String name = null; - if (Character.isUpperCase(peek("node type or name").charAt(0))) { - String token = next(); - type = knownTypes.get(token); - if (type == null) { - throw new RuleParseError("Unknown node type: " + token); - } - if (peek("=").equals("=")) { - next(); - name = next(); - } - originatingElements.addAll(type.originatingElements); - requiredPackages.add(type.nodePackage); - } else if (Character.isLowerCase(peek("name").charAt(0))) { - name = next(); - type = valueType; - } else { - throw new RuleParseError("Unexpected token \"%s\" when looking for name or node type", peek(null)); - } - if (name != null) { - if (!capturedNames.contains(name)) { - capturedNames.add(name); - capturedTypes.add(type); - } else { - int index = capturedNames.indexOf(name); - if (capturedTypes.get(index) != type) { - throw new RuleParseError("Captured node \"%s\" has differing types", name); - } - } - } - return new MatchDescriptor(type, name, forExpression); - } - - List generateVariants() { - return matchDescriptor.generateVariants(); - } - - /** - * Recursively accumulate any required Position declarations. - */ - void generatePositionDeclarations(Set declarations) { - matchDescriptor.generatePositionDeclarations(declarations); - } - - /** - * - * @return the list of node types which are captured by name - */ - public ArrayList capturedTypes() { - return capturedTypes; - } - - public ArrayList capturedNames() { - return capturedNames; - } - } - - /** - * Set to true to enable logging to a local file during annotation processing. There's no normal - * channel for any debug messages and debugging annotation processors requires some special - * setup. - */ - private static final boolean DEBUG = false; - - private PrintWriter log; - - /** - * Logging facility for debugging the annotation processor. - */ - - private PrintWriter getLog() { - if (log == null) { - try { - // Create the log file within the generated source directory so it's easy to find. - // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly - // on the mac. - FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); - log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); - } catch (IOException e) { - // Do nothing - } - } - return log; - } - - private void logMessage(String format, Object... args) { - if (!DEBUG) { - return; - } - PrintWriter bw = getLog(); - if (bw != null) { - bw.printf(format, args); - bw.flush(); - } - } - - private void logException(Throwable t) { - if (!DEBUG) { - return; - } - PrintWriter bw = getLog(); - if (bw != null) { - t.printStackTrace(bw); - bw.flush(); - } - } - - /** - * Bugs in an annotation processor can cause silent failure so try to report any exception - * throws as errors. - */ - private void reportExceptionThrow(Element element, Throwable t) { - if (element != null) { - logMessage("throw for %s:\n", element); - } - logException(t); - errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); - } - - static class TypeDescriptor { - final TypeMirror mirror; - - /** - * The name uses in match expressions to refer to this type. - */ - final String shortName; - - /** - * The simple name of the {@link ValueNode} class represented by this type. - */ - final String nodeClass; - - /** - * The package of {@link ValueNode} class represented by this type. - */ - final String nodePackage; - - /** - * The matchable inputs of the node. - */ - final String[] inputs; - - /** - * Should swapped variants of this match be generated. The user of the match is expected to - * compensate for any ordering differences in compare which are commutative but require - * reinterpreting the condition in that case. - */ - final boolean commutative; - - /** - * Can multiple users of this node subsume it. Constants can be swallowed into a match even - * if there are multiple users. - */ - final boolean shareable; - - final Set originatingElements = new HashSet<>(); - - TypeDescriptor(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, String[] inputs, boolean commutative, boolean shareable) { - this.mirror = mirror; - this.shortName = shortName; - this.nodeClass = nodeClass; - this.nodePackage = nodePackage; - this.inputs = inputs; - this.commutative = commutative; - this.shareable = shareable; - assert !commutative || inputs.length == 2; - } - } - - /** - * The types which are know for purpose of parsing MatchRule expressions. - */ - Map knownTypes = new HashMap<>(); - - /** - * The mapping between elements with MatchRules and the wrapper class used invoke the code - * generation after the match. - */ - private Map invokers = new LinkedHashMap<>(); - - private TypeDescriptor valueType; - - private TypeMirror matchRulesTypeMirror; - - private TypeMirror matchRuleTypeMirror; - - private TypeMirror matchableNodeTypeMirror; - - private TypeMirror matchableNodesTypeMirror; - - private void declareType(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, String[] inputs, boolean commutative, boolean shareable, Element element) { - TypeDescriptor descriptor = new TypeDescriptor(mirror, shortName, nodeClass, nodePackage, inputs, commutative, shareable); - descriptor.originatingElements.add(element); - knownTypes.put(shortName, descriptor); - } - - private String findPackage(Element type) { - PackageElement p = processingEnv.getElementUtils().getPackageOf(type); - if (p != null) { - return p.getQualifiedName().toString(); - } - throw new GraalInternalError("can't find package for %s", type); - } - - class MatchDescriptor { - TypeDescriptor nodeType; - String name; - MatchDescriptor[] inputs; - - MatchDescriptor(TypeDescriptor nodeType, String name, boolean forExpression) { - this.nodeType = nodeType; - this.name = name; - if (forExpression) { - this.inputs = new MatchDescriptor[nodeType.inputs.length]; - } else { - this.inputs = new MatchDescriptor[0]; - } - } - - public void generatePositionDeclarations(Set declarations) { - if (inputs.length == 0) { - return; - } - declarations.add(generatePositionDeclaration()); - for (MatchDescriptor desc : inputs) { - desc.generatePositionDeclarations(declarations); - } - } - - List recurseVariants(int index) { - if (inputs.length == 0) { - return new ArrayList<>(); - } - List currentVariants = inputs[index].generateVariants(); - if (index == inputs.length - 1) { - return currentVariants; - } - List subVariants = recurseVariants(index + 1); - List result = new ArrayList<>(); - for (String current : currentVariants) { - for (String sub : subVariants) { - result.add(current + ", " + sub); - if (nodeType.commutative) { - result.add(sub + ", " + current); - } - } - } - return result; - } - - /** - * Recursively generate all the variants of this rule pattern. Currently that just means to - * swap the inputs for commutative rules, producing all possible permutations. - * - * @return a list of Strings which will construct pattern matchers for this rule. - */ - List generateVariants() { - String prefix = formatPrefix(); - String suffix = formatSuffix(); - ArrayList variants = new ArrayList<>(); - if (inputs.length > 0) { - for (String var : recurseVariants(0)) { - variants.add(prefix + ", " + var + suffix); - } - } else { - assert inputs.length == 0; - variants.add(prefix + suffix); - } - - return variants; - } - - private String formatPrefix() { - if (nodeType == valueType) { - return String.format("new MatchPattern(%s, false", name != null ? ("\"" + name + "\"") : "null"); - } else { - return String.format("new MatchPattern(%s.class, %s", nodeType.nodeClass, name != null ? ("\"" + name + "\"") : "null"); - } - } - - private String formatSuffix() { - if (nodeType != null) { - if (inputs.length != nodeType.inputs.length) { - return ", true)"; - } else { - if (nodeType.inputs.length > 0) { - return ", " + nodeType.nodeClass + "_positions, " + !nodeType.shareable + ")"; - } - if (nodeType.shareable) { - return ", false)"; - } - } - } - return ")"; - } - - String generatePositionDeclaration() { - return String.format("Position[] %s_positions = MatchRuleRegistry.findPositions(%s.TYPE, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass, - String.join("\", \"", nodeType.inputs)); - } - } - - /** - * Strip the package off a class name leaving the full class name including any outer classes. - */ - private String fullClassName(Element element) { - assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE : element; - String pkg = findPackage(element); - return ((TypeElement) element).getQualifiedName().toString().substring(pkg.length() + 1); - } - - private void createFiles(MatchRuleDescriptor info) { - String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString(); - Name topDeclaringClass = info.topDeclaringType.getSimpleName(); - - String matchStatementClassName = topDeclaringClass + "_" + MatchStatementSet.class.getSimpleName(); - Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]); - - Types typeUtils = typeUtils(); - Filer filer = processingEnv.getFiler(); - try (PrintWriter out = createSourceFile(pkg, matchStatementClassName, filer, originatingElements)) { - - out.println("// CheckStyle: stop header check"); - out.println("// GENERATED CONTENT - DO NOT EDIT"); - out.println("// Source: " + topDeclaringClass + ".java"); - out.println("package " + pkg + ";"); - out.println(""); - out.println("import java.util.*;"); - out.println("import " + MatchStatementSet.class.getPackage().getName() + ".*;"); - out.println("import " + NodeLIRBuilder.class.getName() + ";"); - out.println("import " + Position.class.getName() + ";"); - for (String p : info.requiredPackages) { - out.println("import " + p + ".*;"); - } - out.println(""); - - out.println("public class " + matchStatementClassName + " implements " + MatchStatementSet.class.getSimpleName() + " {"); - - out.println(); - - // Generate declarations for the wrapper class to invoke the code generation methods. - for (MethodInvokerItem invoker : invokers.values()) { - StringBuilder args = new StringBuilder(); - StringBuilder types = new StringBuilder(); - int count = invoker.fields.size(); - int index = 0; - for (VariableElement arg : invoker.fields) { - args.append('"'); - args.append(arg.getSimpleName()); - args.append('"'); - types.append(String.format("(%s) args[%s]", fullClassName(typeUtils.asElement(arg.asType())), index++)); - if (count-- > 1) { - args.append(", "); - types.append(", "); - } - } - out.printf(" private static final String[] %s = new String[] {%s};\n", invoker.argumentsListName(), args); - out.printf(" private static final class %s implements MatchGenerator {\n", invoker.wrapperClass()); - out.printf(" static MatchGenerator instance = new %s();\n", invoker.wrapperClass()); - out.printf(" public ComplexMatchResult match(NodeLIRBuilder builder, Object...args) {\n"); - out.printf(" return ((%s) builder).%s(%s);\n", invoker.nodeLIRBuilderClass, invoker.methodName, types); - out.printf(" }\n"); - out.printf(" public String getName() {\n"); - out.printf(" return \"%s\";\n", invoker.methodName); - out.printf(" }\n"); - out.printf(" }\n"); - out.println(); - - } - - String desc = MatchStatement.class.getSimpleName(); - - out.println(" public Class forClass() {"); - out.println(" return " + topDeclaringClass + ".class;"); - out.println(" }"); - out.println(); - out.println(" @Override"); - out.println(" public List<" + desc + "> statements() {"); - out.println(" // Checkstyle: stop "); - - for (String positionDeclaration : info.positionDeclarations) { - out.println(" " + positionDeclaration); - } - out.println(); - - out.println(" List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList("); - - int i = 0; - for (MatchRuleItem matchRule : info.matchRules) { - String comma = i == info.matchRules.size() - 1 ? "" : ","; - out.printf(" %s%s\n", matchRule.ruleBuilder(), comma); - i++; - } - out.println(" ));"); - out.println(" // Checkstyle: resume"); - out.println(" return statements;"); - out.println(" }"); - - out.println(); - - out.println("}"); - } - - try { - createProviderFile(pkg, matchStatementClassName, originatingElements); - } catch (IOException e) { - reportExceptionThrow(info.topDeclaringType, e); - } - } - - private void createProviderFile(String pkg, String providerClassName, Element... originatingElements) throws IOException { - String filename = "META-INF/providers/" + pkg + "." + providerClassName; - FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements); - PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8")); - writer.println(MatchStatementSet.class.getName()); - writer.close(); - } - - protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { - try { - // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle - JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements); - return new PrintWriter(sourceFile.openWriter()) { - - @Override - public void println() { - print("\n"); - } - }; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Used to generate the MatchStatement constructor invocation. - */ - static class MatchRuleItem { - private final String matchPattern; - private final MethodInvokerItem invoker; - - public MatchRuleItem(String matchPattern, MethodInvokerItem invoker) { - this.matchPattern = matchPattern; - this.invoker = invoker; - } - - /** - * @return a string which will construct the MatchStatement instance to match this pattern. - */ - public String ruleBuilder() { - return String.format("new MatchStatement(\"%s\", %s, %s.instance, %s)", invoker.methodName, matchPattern, invoker.wrapperClass(), invoker.argumentsListName()); - } - } - - /** - * Used to generate the wrapper class to invoke the code generation method. - */ - static class MethodInvokerItem { - final String methodName; - final String nodeLIRBuilderClass; - final ExecutableElement method; - final List fields; - - MethodInvokerItem(String methodName, String nodeLIRBuilderClass, ExecutableElement method, List fields) { - this.methodName = methodName; - this.nodeLIRBuilderClass = nodeLIRBuilderClass; - this.method = method; - this.fields = fields; - } - - String wrapperClass() { - return "MatchGenerator_" + methodName; - } - - String argumentsListName() { - return methodName + "_arguments"; - } - } - - static class MatchRuleDescriptor { - - final TypeElement topDeclaringType; - final List matchRules = new ArrayList<>(); - private final Set originatingElements = new HashSet<>(); - public Set positionDeclarations = new LinkedHashSet<>(); - - /** - * The set of packages which must be imported to refer the classes mention in matchRules. - */ - Set requiredPackages = new HashSet<>(); - - public MatchRuleDescriptor(TypeElement topDeclaringType) { - this.topDeclaringType = topDeclaringType; - } - } - - private static TypeElement topDeclaringType(Element element) { - Element enclosing = element.getEnclosingElement(); - if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) { - assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE; - return (TypeElement) element; - } - return topDeclaringType(enclosing); - } - - private AnnotationMirror findAnnotationMirror(Element element, TypeMirror typeMirror) { - for (AnnotationMirror mirror : element.getAnnotationMirrors()) { - if (typeUtils().isSameType(mirror.getAnnotationType(), typeMirror)) { - return mirror; - } - } - return null; - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (roundEnv.processingOver()) { - return true; - } - - logMessage("Starting round %s\n", roundEnv); - matchRulesTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchRules.class.getCanonicalName()).asType(); - matchRuleTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchRule.class.getCanonicalName()).asType(); - - matchableNodeTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchableNode.class.getCanonicalName()).asType(); - matchableNodesTypeMirror = processingEnv.getElementUtils().getTypeElement(MatchableNodes.class.getCanonicalName()).asType(); - - Element currentElement = null; - try { - for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNode.class)) { - logMessage("%s\n", element); - processMatchableNode(element); - } - for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNodes.class)) { - logMessage("%s\n", element); - processMatchableNode(element); - } - // Define a TypeDescriptor for the generic node but don't enter it into the nodeTypes - // table since it shouldn't be mentioned in match rules. - TypeMirror valueTypeMirror = processingEnv.getElementUtils().getTypeElement(ValueNode.class.getName()).asType(); - valueType = new TypeDescriptor(valueTypeMirror, "Value", ValueNode.class.getSimpleName(), ValueNode.class.getPackage().getName(), new String[0], false, false); - - Map map = new LinkedHashMap<>(); - - for (Element element : roundEnv.getElementsAnnotatedWith(MatchRule.class)) { - currentElement = element; - processMatchRule(map, element, findAnnotationMirror(element, matchRuleTypeMirror)); - } - for (Element element : roundEnv.getElementsAnnotatedWith(MatchRules.class)) { - currentElement = element; - processMatchRule(map, element, findAnnotationMirror(element, matchRulesTypeMirror)); - } - - currentElement = null; - for (MatchRuleDescriptor info : map.values()) { - createFiles(info); - } - - } catch (Throwable t) { - reportExceptionThrow(currentElement, t); - } - - return true; - } - - /** - * Build up the type table to be used during parsing of the MatchRule. - */ - private void processMatchableNode(Element element) { - if (!processedMatchableNode.contains(element)) { - try { - processedMatchableNode.add(element); - - AnnotationMirror mirror = findAnnotationMirror(element, matchableNodesTypeMirror); - if (mirror == null) { - mirror = findAnnotationMirror(element, matchableNodeTypeMirror); - } - if (mirror == null) { - return; - } - TypeElement topDeclaringType = topDeclaringType(element); - List mirrors = null; - if (typeUtils().isSameType(mirror.getAnnotationType(), matchableNodesTypeMirror)) { - // Unpack the mirrors for a repeatable annotation - mirrors = getAnnotationValueList(AnnotationMirror.class, mirror, "value"); - } - int i = 0; - for (MatchableNode matchableNode : element.getAnnotationsByType(MatchableNode.class)) { - processMatchableNode(element, topDeclaringType, matchableNode, mirrors != null ? mirrors.get(i++) : mirror); - } - } catch (Throwable t) { - reportExceptionThrow(element, t); - } - } - } - - private void processMatchableNode(Element element, TypeElement topDeclaringType, MatchableNode matchable, AnnotationMirror mirror) throws GraalInternalError { - logMessage("processMatchableNode %s %s %s\n", topDeclaringType, element, matchable); - String nodeClass; - String nodePackage; - TypeMirror nodeClassMirror = null; - try { - matchable.nodeClass(); - } catch (MirroredTypeException e) { - nodeClassMirror = e.getTypeMirror(); - } - if (nodeClassMirror == null) { - throw new GraalInternalError("Can't get mirror for node class %s", element); - } - if (nodeClassMirror.toString().equals(MatchableNode.class.getName())) { - nodeClass = topDeclaringType.getQualifiedName().toString(); - } else { - nodeClass = nodeClassMirror.toString(); - } - TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(nodeClass); - if (typeElement == null) { - errorMessage(element, mirror, "Class \"%s\" cannot be resolved to a type", nodeClass); - return; - } - nodePackage = findPackage(typeElement); - assert nodeClass.startsWith(nodePackage); - nodeClass = nodeClass.substring(nodePackage.length() + 1); - assert nodeClass.endsWith("Node"); - String shortName = nodeClass.substring(0, nodeClass.length() - 4); - - Types typeUtils = processingEnv.getTypeUtils(); - TypeElement nodeClassElement = (TypeElement) typeUtils.asElement(nodeClassMirror); - for (String input : matchable.inputs()) { - boolean ok = false; - TypeElement current = nodeClassElement; - while (!ok && current != null) { - for (Element fieldElement : ElementFilter.fieldsIn(current.getEnclosedElements())) { - if (fieldElement.getSimpleName().toString().equals(input)) { - ok = true; - break; - } - } - TypeMirror theSuper = current.getSuperclass(); - current = (TypeElement) typeUtils.asElement(theSuper); - } - if (!ok) { - errorMessage(element, mirror, "Input named \"%s\" doesn't exist in %s", input, nodeClassElement.getSimpleName()); - } - } - - declareType(nodeClassMirror, shortName, nodeClass, nodePackage, matchable.inputs(), matchable.commutative(), matchable.shareable(), element); - } - - private void processMatchRule(Map map, Element element, AnnotationMirror mirror) { - if (!processedMatchRule.contains(element)) { - try { - processedMatchRule.add(element); - - // The annotation element type should ensure this is true. - assert element instanceof ExecutableElement; - - findMatchableNodes(element); - - TypeElement topDeclaringType = topDeclaringType(element); - MatchRuleDescriptor info = map.get(topDeclaringType); - if (info == null) { - info = new MatchRuleDescriptor(topDeclaringType); - map.put(topDeclaringType, info); - } - List mirrors = null; - if (typeUtils().isSameType(mirror.getAnnotationType(), matchRulesTypeMirror)) { - // Unpack the mirrors for a repeatable annotation - mirrors = getAnnotationValueList(AnnotationMirror.class, mirror, "value"); - } - int i = 0; - for (MatchRule matchRule : element.getAnnotationsByType(MatchRule.class)) { - processMethodMatchRule((ExecutableElement) element, info, matchRule, mirrors != null ? mirrors.get(i++) : mirror); - } - } catch (Throwable t) { - reportExceptionThrow(element, t); - } - } - } - - /** - * Search the super types of element for MatchableNode definitions. Any superclass or super - * interface can contain definitions of matchable nodes. - * - * @param element - */ - private void findMatchableNodes(Element element) { - processMatchableNode(element); - Element enclosing = element.getEnclosingElement(); - while (enclosing != null) { - if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { - TypeElement current = (TypeElement) enclosing; - while (current != null) { - processMatchableNode(current); - for (TypeMirror intf : current.getInterfaces()) { - Element interfaceElement = typeUtils().asElement(intf); - processMatchableNode(interfaceElement); - // Recurse - findMatchableNodes(interfaceElement); - } - TypeMirror theSuper = current.getSuperclass(); - current = (TypeElement) typeUtils().asElement(theSuper); - } - } - enclosing = enclosing.getEnclosingElement(); - } - } - - private Types typeUtils() { - return processingEnv.getTypeUtils(); - } - - private void processMethodMatchRule(ExecutableElement method, MatchRuleDescriptor info, MatchRule matchRule, AnnotationMirror mirror) { - logMessage("processMethodMatchRule %s %s\n", method, mirror); - - Types typeUtils = typeUtils(); - - if (!method.getModifiers().contains(Modifier.PUBLIC)) { - errorMessage(method, "MatchRule method %s must be public", method.getSimpleName()); - return; - } - if (method.getModifiers().contains(Modifier.STATIC)) { - errorMessage(method, "MatchRule method %s must be non-static", method.getSimpleName()); - return; - } - - try { - TypeMirror returnType = method.getReturnType(); - if (!typeUtils.isSameType(returnType, processingEnv.getElementUtils().getTypeElement(ComplexMatchResult.class.getName()).asType())) { - errorMessage(method, "MatchRule method return type must be %s", ComplexMatchResult.class.getName()); - return; - } - - String rule = matchRule.value(); - RuleParser parser = new RuleParser(rule); - ArrayList expectedTypes = parser.capturedTypes(); - ArrayList expectedNames = parser.capturedNames(); - List actualParameters = method.getParameters(); - if (expectedTypes.size() + 1 < actualParameters.size()) { - errorMessage(method, "Too many arguments for match method %s != %s", expectedTypes.size() + 1, actualParameters.size()); - return; - } - - // Walk through the parameters to the method and see if they exist in the match rule. - // The order doesn't matter but only names mentioned in the rule can be used and they - // must be assignment compatible. - for (VariableElement parameter : actualParameters) { - String name = parameter.getSimpleName().toString(); - int nameIndex = expectedNames.indexOf(name); - if (nameIndex == -1) { - errorMessage(method, "Argument \"%s\" isn't captured in the match rule", name); - return; - } - TypeMirror type = parameter.asType(); - if (!typeUtils.isAssignable(expectedTypes.get(nameIndex).mirror, type)) { - errorMessage(method, "Captured value \"%s\" of type %s is not assignable to argument of type %s", name, expectedTypes.get(nameIndex).mirror, type); - return; - } - } - - String methodName = method.getSimpleName().toString(); - MethodInvokerItem invoker = invokers.get(methodName); - if (invoker == null) { - invoker = new MethodInvokerItem(methodName, topDeclaringType(method).getSimpleName().toString(), method, actualParameters); - invokers.put(methodName, invoker); - } else if (invoker.method != method) { - // This could be supported but it's easier if they are unique since the names - // are used in log output and snippet counters. - errorMessage(method, "Use unique method names for match methods."); - return; - } - - Element enclosing = method.getEnclosingElement(); - String declaringClass = ""; - String separator = ""; - Set originatingElementsList = info.originatingElements; - originatingElementsList.add(method); - while (enclosing != null) { - if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) { - if (enclosing.getModifiers().contains(Modifier.PRIVATE)) { - errorMessage(method, "MatchRule cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing); - return; - } - originatingElementsList.add(enclosing); - declaringClass = enclosing.getSimpleName() + separator + declaringClass; - separator = "."; - } else { - assert enclosing.getKind() == ElementKind.PACKAGE; - } - enclosing = enclosing.getEnclosingElement(); - } - - originatingElementsList.addAll(parser.originatingElements); - info.requiredPackages.addAll(parser.requiredPackages); - - // Accumulate any position declarations. - parser.generatePositionDeclarations(info.positionDeclarations); - - List matches = parser.generateVariants(); - for (String match : matches) { - info.matchRules.add(new MatchRuleItem(match, invoker)); - } - } catch (RuleParseError e) { - errorMessage(method, mirror, e.getMessage()); - } - } - - private void errorMessage(Element element, String format, Object... args) { - processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); - } - - private void errorMessage(Element element, AnnotationMirror mirror, String format, Object... args) { - processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element, mirror); - } - - // TODO borrowed from com.oracle.truffle.dsl.processor.Utils - @SuppressWarnings("unchecked") - private static List getAnnotationValueList(Class expectedListType, AnnotationMirror mirror, String name) { - List values = getAnnotationValue(List.class, mirror, name); - List result = new ArrayList<>(); - - if (values != null) { - for (AnnotationValue value : values) { - T annotationValue = resolveAnnotationValue(expectedListType, value); - if (annotationValue != null) { - result.add(annotationValue); - } - } - } - return result; - } - - private static T getAnnotationValue(Class expectedType, AnnotationMirror mirror, String name) { - return resolveAnnotationValue(expectedType, getAnnotationValue(mirror, name)); - } - - @SuppressWarnings({"unchecked"}) - private static T resolveAnnotationValue(Class expectedType, AnnotationValue value) { - if (value == null) { - return null; - } - - Object unboxedValue = value.accept(new AnnotationValueVisitorImpl(), null); - if (unboxedValue != null) { - if (expectedType == TypeMirror.class && unboxedValue instanceof String) { - return null; - } - if (!expectedType.isAssignableFrom(unboxedValue.getClass())) { - throw new ClassCastException(unboxedValue.getClass().getName() + " not assignable from " + expectedType.getName()); - } - } - return (T) unboxedValue; - } - - private static AnnotationValue getAnnotationValue(AnnotationMirror mirror, String name) { - ExecutableElement valueMethod = null; - for (ExecutableElement method : ElementFilter.methodsIn(mirror.getAnnotationType().asElement().getEnclosedElements())) { - if (method.getSimpleName().toString().equals(name)) { - valueMethod = method; - break; - } - } - - if (valueMethod == null) { - return null; - } - - AnnotationValue value = mirror.getElementValues().get(valueMethod); - if (value == null) { - value = valueMethod.getDefaultValue(); - } - - return value; - } - - private static class AnnotationValueVisitorImpl extends AbstractAnnotationValueVisitor7 { - - @Override - public Object visitBoolean(boolean b, Void p) { - return Boolean.valueOf(b); - } - - @Override - public Object visitByte(byte b, Void p) { - return Byte.valueOf(b); - } - - @Override - public Object visitChar(char c, Void p) { - return c; - } - - @Override - public Object visitDouble(double d, Void p) { - return d; - } - - @Override - public Object visitFloat(float f, Void p) { - return f; - } - - @Override - public Object visitInt(int i, Void p) { - return i; - } - - @Override - public Object visitLong(long i, Void p) { - return i; - } - - @Override - public Object visitShort(short s, Void p) { - return s; - } - - @Override - public Object visitString(String s, Void p) { - return s; - } - - @Override - public Object visitType(TypeMirror t, Void p) { - return t; - } - - @Override - public Object visitEnumConstant(VariableElement c, Void p) { - return c; - } - - @Override - public Object visitAnnotation(AnnotationMirror a, Void p) { - return a; - } - - @Override - public Object visitArray(List vals, Void p) { - return vals; - } - - } -} diff -r 71e05c3a1e12 -r 391f94d4d23f graal/com.oracle.graal.hotspotvmconfig.processor/src/META-INF/services/javax.annotation.processing.Processor --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig.processor/src/META-INF/services/javax.annotation.processing.Processor Thu Apr 23 14:48:52 2015 +0200 @@ -0,0 +1,1 @@ +com.oracle.graal.hotspotvmconfig.processor.HotSpotVMConfigProcessor diff -r 71e05c3a1e12 -r 391f94d4d23f graal/com.oracle.graal.hotspotvmconfig.processor/src/com/oracle/graal/hotspotvmconfig/processor/HotSpotVMConfigProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig.processor/src/com/oracle/graal/hotspotvmconfig/processor/HotSpotVMConfigProcessor.java Thu Apr 23 14:48:52 2015 +0200 @@ -0,0 +1,424 @@ +/* + * 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.hotspotvmconfig.processor; + +import java.io.*; +import java.lang.annotation.*; +import java.util.*; +import java.util.Map.Entry; +import java.util.function.*; + +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; +import javax.tools.*; + +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.hotspotvmconfig.*; + +@SupportedAnnotationTypes({"com.oracle.graal.hotspotvmconfig.HotSpotVMConstant", "com.oracle.graal.hotspotvmconfig.HotSpotVMFlag", "com.oracle.graal.hotspotvmconfig.HotSpotVMField", + "com.oracle.graal.hotspotvmconfig.HotSpotVMType", "com.oracle.graal.hotspotvmconfig.HotSpotVMValue"}) +public class HotSpotVMConfigProcessor extends AbstractProcessor { + + public HotSpotVMConfigProcessor() { + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + /** + * Set to true to enable logging to a local file during annotation processing. There's no normal + * channel for any debug messages and debugging annotation processors requires some special + * setup. + */ + private static final boolean DEBUG = false; + + private PrintWriter log; + + /** + * Logging facility for debugging the annotation processor. + */ + + private PrintWriter getLog() { + if (log == null) { + try { + // Create the log file within the generated source directory so it's easy to find. + // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly + // on the mac. + FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); + log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); + } catch (IOException e) { + // Do nothing + } + } + return log; + } + + private void logMessage(String format, Object... args) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + bw.printf(format, args); + bw.flush(); + } + } + + private void logException(Throwable t) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + t.printStackTrace(bw); + bw.flush(); + } + } + + /** + * Bugs in an annotation processor can cause silent failure so try to report any exception + * throws as errors. + */ + private void reportExceptionThrow(Element element, Throwable t) { + if (element != null) { + logMessage("throw for %s:\n", element); + } + logException(t); + errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); + } + + //@formatter:off + String[] prologue = new String[]{ + "// The normal wrappers CommandLineFlags::boolAt and CommandLineFlags::intxAt skip constant flags", + "static bool boolAt(char* name, bool* value) {", + " Flag* result = Flag::find_flag(name, strlen(name), true, true);", + " if (result == NULL) return false;", + " if (!result->is_bool()) return false;", + " *value = result->get_bool();", + " return true;", + "}", + "", + "static bool intxAt(char* name, intx* value) {", + " Flag* result = Flag::find_flag(name, strlen(name), true, true);", + " if (result == NULL) return false;", + " if (!result->is_intx()) return false;", + " *value = result->get_intx();", + " return true;", + "}", + "", + "#define set_boolean(name, value) vmconfig_oop->bool_field_put(fs.offset(), value)", + "#define set_byte(name, value) vmconfig_oop->byte_field_put(fs.offset(), (jbyte)(value))", + "#define set_short(name, value) vmconfig_oop->short_field_put(fs.offset(), (jshort)(value))", + "#define set_int(name, value) vmconfig_oop->int_field_put(fs.offset(), (int)(value))", + "#define set_long(name, value) vmconfig_oop->long_field_put(fs.offset(), value)", + "#define set_address(name, value) do { set_long(name, (jlong)(value)); } while (0)", + "", + "#define set_optional_boolean_flag(varName, flagName) do { bool flagValue; if (boolAt((char*) flagName, &flagValue)) { set_boolean(varName, flagValue); } } while (0)", + "#define set_optional_int_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_int(varName, flagValue); } } while (0)", + "#define set_optional_long_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_long(varName, flagValue); } } while (0)", + "", + "void VMStructs::initHotSpotVMConfig(oop vmconfig_oop) {", + " InstanceKlass* vmconfig_klass = InstanceKlass::cast(vmconfig_oop->klass());", + "", + }; + //@formatter:on + + String outputName = "HotSpotVMConfig.inline.hpp"; + String outputDirectory = "hotspot"; + + private void createFiles(Map annotations, Element element) { + + Filer filer = processingEnv.getFiler(); + try (PrintWriter out = createSourceFile(outputDirectory, outputName, filer, element)) { + + for (String line : prologue) { + out.println(line); + } + + Map expectedValues = new HashMap<>(); + for (VMConfigField value : annotations.values()) { + if (!value.optional) { + String key = value.define != null ? value.define : ""; + if (expectedValues.get(key) == null) { + expectedValues.put(key, 1); + } else { + expectedValues.put(key, expectedValues.get(key) + 1); + } + } + } + + out.printf(" int expected = %s;%n", expectedValues.get("")); + for (Entry entry : expectedValues.entrySet()) { + if (entry.getKey().equals("")) { + continue; + } + out.printf("#if %s%n", entry.getKey()); + out.printf(" expected += %s;%n", entry.getValue()); + out.printf("#endif%n"); + } + out.println(" int assigned = 0;"); + out.println(" for (JavaFieldStream fs(vmconfig_klass); !fs.done(); fs.next()) {"); + + Set fieldTypes = new HashSet<>(); + for (VMConfigField key : annotations.values()) { + fieldTypes.add(key.getType()); + } + // For each type of field, generate a switch on the length of the symbol and then do a + // direct compare. In general this reduces each operation to 2 tests plus a string + // compare. Being more perfect than that is probably not worth it. + for (String type : fieldTypes) { + String sigtype = type.equals("boolean") ? "bool" : type; + out.println(" if (fs.signature() == vmSymbols::" + sigtype + "_signature()) {"); + Set lengths = new HashSet<>(); + for (Entry entry : annotations.entrySet()) { + if (entry.getValue().getType().equals(type)) { + lengths.add(entry.getKey().length()); + } + } + out.println(" switch (fs.name()->utf8_length()) {"); + for (int len : lengths) { + out.println(" case " + len + ":"); + for (Entry entry : annotations.entrySet()) { + if (entry.getValue().getType().equals(type) && entry.getKey().length() == len) { + out.println(" if (fs.name()->equals(\"" + entry.getKey() + "\")) {"); + entry.getValue().emit(out); + out.println(" continue;"); + out.println(" }"); + } + } + out.println(" continue;"); + } + out.println(" } // switch"); + out.println(" continue;"); + out.println(" } // if"); + } + out.println(" } // for"); + out.println(" guarantee(assigned == expected, \"Didn't find all fields during init of HotSpotVMConfig. Maybe recompile?\");"); + out.println("}"); + } + } + + protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { + try { + // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle + FileObject sourceFile = filer.createResource(StandardLocation.SOURCE_OUTPUT, pkg, relativeName, originatingElements); + logMessage("%s\n", sourceFile); + return new PrintWriter(sourceFile.openWriter()) { + + @Override + public void println() { + print("\n"); + } + }; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static class VMConfigField { + final String setter; + final String define; + private boolean optional; + final VariableElement field; + + public VMConfigField(VariableElement field, HotSpotVMField value) { + this.field = field; + define = archDefines(value.archs()); + String type = field.asType().toString(); + String name = value.name(); + int i = name.lastIndexOf("::"); + switch (value.get()) { + case OFFSET: + setter = String.format("set_%s(\"%s\", offset_of(%s, %s));", type, field.getSimpleName(), name.substring(0, i), name.substring(i + 2)); + break; + case ADDRESS: + setter = String.format("set_address(\"%s\", &%s);", field.getSimpleName(), name); + break; + case VALUE: + setter = String.format("set_%s(\"%s\", (%s) (intptr_t) %s);", type, field.getSimpleName(), type, name); + break; + default: + throw new GraalInternalError("unexpected type: " + value.get()); + } + } + + public VMConfigField(VariableElement field, HotSpotVMType value) { + this.field = field; + define = null; // ((HotSpotVMType) annotation).archs(); + String type = field.asType().toString(); + setter = String.format("set_%s(\"%s\", sizeof(%s));", type, field.getSimpleName(), value.name()); + } + + public VMConfigField(VariableElement field, HotSpotVMValue value) { + this.field = field; + String[] defines = value.defines(); + int length = defines.length; + if (length != 0) { + for (int i = 0; i < length; i++) { + defines[i] = "defined(" + defines[i] + ")"; + } + define = String.join(" || ", defines); + } else { + define = null; // ((HotSpotVMValue) annotation).archs(); + } + String type = field.asType().toString(); + if (value.get() == HotSpotVMValue.Type.ADDRESS) { + setter = String.format("set_address(\"%s\", %s);", field.getSimpleName(), value.expression()); + } else { + setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.expression()); + } + } + + public VMConfigField(VariableElement field, HotSpotVMConstant value) { + this.field = field; + define = archDefines(value.archs()); + String type = field.asType().toString(); + setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); + } + + public VMConfigField(VariableElement field, HotSpotVMFlag value) { + this.field = field; + define = archDefines(value.archs()); + optional = value.optional(); + String type = field.asType().toString(); + if (value.optional()) { + setter = String.format("set_optional_%s_flag(\"%s\", \"%s\");", type, field.getSimpleName(), value.name()); + } else { + setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); + } + } + + public String getType() { + return field.asType().toString(); + } + + private static String archDefine(String arch) { + switch (arch) { + case "amd64": + return "defined(AMD64)"; + case "sparcv9": + return "(defined(SPARC) && defined(_LP64))"; + case "sparc": + return "defined(SPARC)"; + default: + throw new GraalInternalError("unexpected arch: " + arch); + } + } + + private static String archDefines(String[] archs) { + if (archs == null || archs.length == 0) { + return null; + } + if (archs.length == 1) { + return archDefine(archs[0]); + } + String[] defs = new String[archs.length]; + int i = 0; + for (String arch : archs) { + defs[i++] = archDefine(arch); + } + return String.join(" || ", defs); + } + + public void emit(PrintWriter out) { + if (define != null) { + out.printf("#if %s\n", define); + } + out.printf(" %s%n", setter); + if (!optional) { + out.printf(" assigned++;%n"); + } + if (define != null) { + out.printf("#endif\n"); + } + } + + } + + @SuppressWarnings("unchecked") + private void collectAnnotations(RoundEnvironment roundEnv, Map annotationMap, Class annotationClass, + BiFunction builder) { + for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) { + Annotation constant = element.getAnnotation(annotationClass); + if (element.getKind() != ElementKind.FIELD) { + errorMessage(element, "%s annotations may only be on fields", annotationClass.getSimpleName()); + } + if (annotationClass == HotSpotVMValue.class) { + HotSpotVMValue value = (HotSpotVMValue) constant; + if (value.get() == HotSpotVMValue.Type.ADDRESS && !element.asType().toString().equals("long")) { + errorMessage(element, "HotSpotVMValue with get == ADDRESS must be of type long, but found %s", element.asType()); + } + } + if (currentTypeElement == null) { + currentTypeElement = element.getEnclosingElement(); + } else { + if (!currentTypeElement.equals(element.getEnclosingElement())) { + errorMessage(element, "Multiple types encountered. Only HotSpotVMConfig is supported"); + } + } + annotationMap.put(element.getSimpleName().toString(), builder.apply((VariableElement) element, (T) constant)); + } + } + + private void errorMessage(Element element, String format, Object... args) { + processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); + } + + Element currentTypeElement = null; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return true; + } + logMessage("Starting round %s %s\n", roundEnv, annotations); + try { + + currentTypeElement = null; + + // First collect all the annotations. + Map annotationMap = new HashMap<>(); + collectAnnotations(roundEnv, annotationMap, HotSpotVMConstant.class, (e, v) -> new VMConfigField(e, v)); + collectAnnotations(roundEnv, annotationMap, HotSpotVMFlag.class, (e, v) -> new VMConfigField(e, v)); + collectAnnotations(roundEnv, annotationMap, HotSpotVMField.class, (e, v) -> new VMConfigField(e, v)); + collectAnnotations(roundEnv, annotationMap, HotSpotVMType.class, (e, v) -> new VMConfigField(e, v)); + collectAnnotations(roundEnv, annotationMap, HotSpotVMValue.class, (e, v) -> new VMConfigField(e, v)); + + if (annotationMap.isEmpty()) { + return true; + } + + logMessage("type element %s\n", currentTypeElement); + createFiles(annotationMap, currentTypeElement); + + } catch (Throwable t) { + reportExceptionThrow(null, t); + } + + return true; + } +} diff -r 71e05c3a1e12 -r 391f94d4d23f graal/com.oracle.graal.hotspotvmconfig/src/META-INF/services/javax.annotation.processing.Processor --- a/graal/com.oracle.graal.hotspotvmconfig/src/META-INF/services/javax.annotation.processing.Processor Tue Apr 21 17:32:06 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -com.oracle.graal.hotspotvmconfig.HotSpotVMConfigProcessor diff -r 71e05c3a1e12 -r 391f94d4d23f graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConfigProcessor.java --- a/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConfigProcessor.java Tue Apr 21 17:32:06 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,423 +0,0 @@ -/* - * 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.hotspotvmconfig; - -import java.io.*; -import java.lang.annotation.*; -import java.util.*; -import java.util.Map.Entry; -import java.util.function.*; - -import javax.annotation.processing.*; -import javax.lang.model.*; -import javax.lang.model.element.*; -import javax.tools.Diagnostic.Kind; -import javax.tools.*; - -import com.oracle.graal.compiler.common.*; - -@SupportedAnnotationTypes({"com.oracle.graal.hotspotvmconfig.HotSpotVMConstant", "com.oracle.graal.hotspotvmconfig.HotSpotVMFlag", "com.oracle.graal.hotspotvmconfig.HotSpotVMField", - "com.oracle.graal.hotspotvmconfig.HotSpotVMType", "com.oracle.graal.hotspotvmconfig.HotSpotVMValue"}) -public class HotSpotVMConfigProcessor extends AbstractProcessor { - - public HotSpotVMConfigProcessor() { - } - - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - /** - * Set to true to enable logging to a local file during annotation processing. There's no normal - * channel for any debug messages and debugging annotation processors requires some special - * setup. - */ - private static final boolean DEBUG = false; - - private PrintWriter log; - - /** - * Logging facility for debugging the annotation processor. - */ - - private PrintWriter getLog() { - if (log == null) { - try { - // Create the log file within the generated source directory so it's easy to find. - // /tmp isn't platform independent and java.io.tmpdir can map anywhere, particularly - // on the mac. - FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log"); - log = new PrintWriter(new FileWriter(file.toUri().getPath(), true)); - } catch (IOException e) { - // Do nothing - } - } - return log; - } - - private void logMessage(String format, Object... args) { - if (!DEBUG) { - return; - } - PrintWriter bw = getLog(); - if (bw != null) { - bw.printf(format, args); - bw.flush(); - } - } - - private void logException(Throwable t) { - if (!DEBUG) { - return; - } - PrintWriter bw = getLog(); - if (bw != null) { - t.printStackTrace(bw); - bw.flush(); - } - } - - /** - * Bugs in an annotation processor can cause silent failure so try to report any exception - * throws as errors. - */ - private void reportExceptionThrow(Element element, Throwable t) { - if (element != null) { - logMessage("throw for %s:\n", element); - } - logException(t); - errorMessage(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4))); - } - - //@formatter:off - String[] prologue = new String[]{ - "// The normal wrappers CommandLineFlags::boolAt and CommandLineFlags::intxAt skip constant flags", - "static bool boolAt(char* name, bool* value) {", - " Flag* result = Flag::find_flag(name, strlen(name), true, true);", - " if (result == NULL) return false;", - " if (!result->is_bool()) return false;", - " *value = result->get_bool();", - " return true;", - "}", - "", - "static bool intxAt(char* name, intx* value) {", - " Flag* result = Flag::find_flag(name, strlen(name), true, true);", - " if (result == NULL) return false;", - " if (!result->is_intx()) return false;", - " *value = result->get_intx();", - " return true;", - "}", - "", - "#define set_boolean(name, value) vmconfig_oop->bool_field_put(fs.offset(), value)", - "#define set_byte(name, value) vmconfig_oop->byte_field_put(fs.offset(), (jbyte)(value))", - "#define set_short(name, value) vmconfig_oop->short_field_put(fs.offset(), (jshort)(value))", - "#define set_int(name, value) vmconfig_oop->int_field_put(fs.offset(), (int)(value))", - "#define set_long(name, value) vmconfig_oop->long_field_put(fs.offset(), value)", - "#define set_address(name, value) do { set_long(name, (jlong)(value)); } while (0)", - "", - "#define set_optional_boolean_flag(varName, flagName) do { bool flagValue; if (boolAt((char*) flagName, &flagValue)) { set_boolean(varName, flagValue); } } while (0)", - "#define set_optional_int_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_int(varName, flagValue); } } while (0)", - "#define set_optional_long_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_long(varName, flagValue); } } while (0)", - "", - "void VMStructs::initHotSpotVMConfig(oop vmconfig_oop) {", - " InstanceKlass* vmconfig_klass = InstanceKlass::cast(vmconfig_oop->klass());", - "", - }; - //@formatter:on - - String outputName = "HotSpotVMConfig.inline.hpp"; - String outputDirectory = "hotspot"; - - private void createFiles(Map annotations, Element element) { - - Filer filer = processingEnv.getFiler(); - try (PrintWriter out = createSourceFile(outputDirectory, outputName, filer, element)) { - - for (String line : prologue) { - out.println(line); - } - - Map expectedValues = new HashMap<>(); - for (VMConfigField value : annotations.values()) { - if (!value.optional) { - String key = value.define != null ? value.define : ""; - if (expectedValues.get(key) == null) { - expectedValues.put(key, 1); - } else { - expectedValues.put(key, expectedValues.get(key) + 1); - } - } - } - - out.printf(" int expected = %s;%n", expectedValues.get("")); - for (Entry entry : expectedValues.entrySet()) { - if (entry.getKey().equals("")) { - continue; - } - out.printf("#if %s%n", entry.getKey()); - out.printf(" expected += %s;%n", entry.getValue()); - out.printf("#endif%n"); - } - out.println(" int assigned = 0;"); - out.println(" for (JavaFieldStream fs(vmconfig_klass); !fs.done(); fs.next()) {"); - - Set fieldTypes = new HashSet<>(); - for (VMConfigField key : annotations.values()) { - fieldTypes.add(key.getType()); - } - // For each type of field, generate a switch on the length of the symbol and then do a - // direct compare. In general this reduces each operation to 2 tests plus a string - // compare. Being more perfect than that is probably not worth it. - for (String type : fieldTypes) { - String sigtype = type.equals("boolean") ? "bool" : type; - out.println(" if (fs.signature() == vmSymbols::" + sigtype + "_signature()) {"); - Set lengths = new HashSet<>(); - for (Entry entry : annotations.entrySet()) { - if (entry.getValue().getType().equals(type)) { - lengths.add(entry.getKey().length()); - } - } - out.println(" switch (fs.name()->utf8_length()) {"); - for (int len : lengths) { - out.println(" case " + len + ":"); - for (Entry entry : annotations.entrySet()) { - if (entry.getValue().getType().equals(type) && entry.getKey().length() == len) { - out.println(" if (fs.name()->equals(\"" + entry.getKey() + "\")) {"); - entry.getValue().emit(out); - out.println(" continue;"); - out.println(" }"); - } - } - out.println(" continue;"); - } - out.println(" } // switch"); - out.println(" continue;"); - out.println(" } // if"); - } - out.println(" } // for"); - out.println(" guarantee(assigned == expected, \"Didn't find all fields during init of HotSpotVMConfig. Maybe recompile?\");"); - out.println("}"); - } - } - - protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { - try { - // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle - FileObject sourceFile = filer.createResource(StandardLocation.SOURCE_OUTPUT, pkg, relativeName, originatingElements); - logMessage("%s\n", sourceFile); - return new PrintWriter(sourceFile.openWriter()) { - - @Override - public void println() { - print("\n"); - } - }; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - static class VMConfigField { - final String setter; - final String define; - private boolean optional; - final VariableElement field; - - public VMConfigField(VariableElement field, HotSpotVMField value) { - this.field = field; - define = archDefines(value.archs()); - String type = field.asType().toString(); - String name = value.name(); - int i = name.lastIndexOf("::"); - switch (value.get()) { - case OFFSET: - setter = String.format("set_%s(\"%s\", offset_of(%s, %s));", type, field.getSimpleName(), name.substring(0, i), name.substring(i + 2)); - break; - case ADDRESS: - setter = String.format("set_address(\"%s\", &%s);", field.getSimpleName(), name); - break; - case VALUE: - setter = String.format("set_%s(\"%s\", (%s) (intptr_t) %s);", type, field.getSimpleName(), type, name); - break; - default: - throw new GraalInternalError("unexpected type: " + value.get()); - } - } - - public VMConfigField(VariableElement field, HotSpotVMType value) { - this.field = field; - define = null; // ((HotSpotVMType) annotation).archs(); - String type = field.asType().toString(); - setter = String.format("set_%s(\"%s\", sizeof(%s));", type, field.getSimpleName(), value.name()); - } - - public VMConfigField(VariableElement field, HotSpotVMValue value) { - this.field = field; - String[] defines = value.defines(); - int length = defines.length; - if (length != 0) { - for (int i = 0; i < length; i++) { - defines[i] = "defined(" + defines[i] + ")"; - } - define = String.join(" || ", defines); - } else { - define = null; // ((HotSpotVMValue) annotation).archs(); - } - String type = field.asType().toString(); - if (value.get() == HotSpotVMValue.Type.ADDRESS) { - setter = String.format("set_address(\"%s\", %s);", field.getSimpleName(), value.expression()); - } else { - setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.expression()); - } - } - - public VMConfigField(VariableElement field, HotSpotVMConstant value) { - this.field = field; - define = archDefines(value.archs()); - String type = field.asType().toString(); - setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); - } - - public VMConfigField(VariableElement field, HotSpotVMFlag value) { - this.field = field; - define = archDefines(value.archs()); - optional = value.optional(); - String type = field.asType().toString(); - if (value.optional()) { - setter = String.format("set_optional_%s_flag(\"%s\", \"%s\");", type, field.getSimpleName(), value.name()); - } else { - setter = String.format("set_%s(\"%s\", %s);", type, field.getSimpleName(), value.name()); - } - } - - public String getType() { - return field.asType().toString(); - } - - private static String archDefine(String arch) { - switch (arch) { - case "amd64": - return "defined(AMD64)"; - case "sparcv9": - return "(defined(SPARC) && defined(_LP64))"; - case "sparc": - return "defined(SPARC)"; - default: - throw new GraalInternalError("unexpected arch: " + arch); - } - } - - private static String archDefines(String[] archs) { - if (archs == null || archs.length == 0) { - return null; - } - if (archs.length == 1) { - return archDefine(archs[0]); - } - String[] defs = new String[archs.length]; - int i = 0; - for (String arch : archs) { - defs[i++] = archDefine(arch); - } - return String.join(" || ", defs); - } - - public void emit(PrintWriter out) { - if (define != null) { - out.printf("#if %s\n", define); - } - out.printf(" %s%n", setter); - if (!optional) { - out.printf(" assigned++;%n"); - } - if (define != null) { - out.printf("#endif\n"); - } - } - - } - - @SuppressWarnings("unchecked") - private void collectAnnotations(RoundEnvironment roundEnv, Map annotationMap, Class annotationClass, - BiFunction builder) { - for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) { - Annotation constant = element.getAnnotation(annotationClass); - if (element.getKind() != ElementKind.FIELD) { - errorMessage(element, "%s annotations may only be on fields", annotationClass.getSimpleName()); - } - if (annotationClass == HotSpotVMValue.class) { - HotSpotVMValue value = (HotSpotVMValue) constant; - if (value.get() == HotSpotVMValue.Type.ADDRESS && !element.asType().toString().equals("long")) { - errorMessage(element, "HotSpotVMValue with get == ADDRESS must be of type long, but found %s", element.asType()); - } - } - if (currentTypeElement == null) { - currentTypeElement = element.getEnclosingElement(); - } else { - if (!currentTypeElement.equals(element.getEnclosingElement())) { - errorMessage(element, "Multiple types encountered. Only HotSpotVMConfig is supported"); - } - } - annotationMap.put(element.getSimpleName().toString(), builder.apply((VariableElement) element, (T) constant)); - } - } - - private void errorMessage(Element element, String format, Object... args) { - processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); - } - - Element currentTypeElement = null; - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (roundEnv.processingOver()) { - return true; - } - logMessage("Starting round %s %s\n", roundEnv, annotations); - try { - - currentTypeElement = null; - - // First collect all the annotations. - Map annotationMap = new HashMap<>(); - collectAnnotations(roundEnv, annotationMap, HotSpotVMConstant.class, (e, v) -> new VMConfigField(e, v)); - collectAnnotations(roundEnv, annotationMap, HotSpotVMFlag.class, (e, v) -> new VMConfigField(e, v)); - collectAnnotations(roundEnv, annotationMap, HotSpotVMField.class, (e, v) -> new VMConfigField(e, v)); - collectAnnotations(roundEnv, annotationMap, HotSpotVMType.class, (e, v) -> new VMConfigField(e, v)); - collectAnnotations(roundEnv, annotationMap, HotSpotVMValue.class, (e, v) -> new VMConfigField(e, v)); - - if (annotationMap.isEmpty()) { - return true; - } - - logMessage("type element %s\n", currentTypeElement); - createFiles(annotationMap, currentTypeElement); - - } catch (Throwable t) { - reportExceptionThrow(null, t); - } - - return true; - } -} diff -r 71e05c3a1e12 -r 391f94d4d23f mx/suite.py --- a/mx/suite.py Tue Apr 21 17:32:06 2015 +0200 +++ b/mx/suite.py Thu Apr 23 14:48:52 2015 +0200 @@ -314,13 +314,20 @@ "com.oracle.graal.hotspotvmconfig" : { "subDir" : "graal", "sourceDirs" : ["src"], - "dependencies" : ["com.oracle.graal.compiler.common"], "checkstyle" : "com.oracle.graal.graph", - "annotationProcessors" : ["com.oracle.graal.service.processor"], "javaCompliance" : "1.8", "workingSets" : "Graal,HotSpot", }, + "com.oracle.graal.hotspotvmconfig.processor" : { + "subDir" : "graal", + "sourceDirs" : ["src"], + "dependencies" : ["com.oracle.graal.hotspotvmconfig", "com.oracle.graal.compiler.common"], + "checkstyle" : "com.oracle.graal.graph", + "javaCompliance" : "1.8", + "workingSets" : "Graal,HotSpot,Codegen", + }, + "com.oracle.graal.hotspot" : { "subDir" : "graal", "sourceDirs" : ["src"], @@ -334,6 +341,7 @@ "annotationProcessors" : [ "com.oracle.graal.replacements.verifier", "com.oracle.graal.service.processor", + "com.oracle.graal.hotspotvmconfig.processor", ], "javaCompliance" : "1.8", "workingSets" : "Graal,HotSpot", @@ -393,7 +401,7 @@ "com.oracle.graal.replacements.sparc", ], "checkstyle" : "com.oracle.graal.graph", - "annotationProcessors" : ["com.oracle.graal.service.processor"], + "annotationProcessors" : ["com.oracle.graal.service.processor", "com.oracle.graal.compiler.match.processor"], "javaCompliance" : "1.8", "workingSets" : "Graal,HotSpot,SPARC", }, @@ -743,6 +751,17 @@ "workingSets" : "Graal", }, + "com.oracle.graal.compiler.match.processor" : { + "subDir" : "graal", + "sourceDirs" : ["src"], + "dependencies" : [ + "com.oracle.graal.compiler", + ], + "checkstyle" : "com.oracle.graal.graph", + "javaCompliance" : "1.8", + "workingSets" : "Graal,Codegen", + }, + "com.oracle.graal.compiler.amd64" : { "subDir" : "graal", "sourceDirs" : ["src"], @@ -751,6 +770,7 @@ "com.oracle.graal.lir.amd64", ], "checkstyle" : "com.oracle.graal.graph", + "annotationProcessors" : ["com.oracle.graal.compiler.match.processor"], "javaCompliance" : "1.8", "workingSets" : "Graal,AMD64", },