1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.core.match.processor;
  26 
  27 import java.io.FileWriter;
  28 import java.io.IOException;
  29 import java.io.PrintWriter;
  30 import java.util.ArrayList;
  31 import java.util.Arrays;
  32 import java.util.Collections;
  33 import java.util.HashMap;
  34 import java.util.HashSet;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.Set;
  38 import java.util.regex.Matcher;
  39 import java.util.regex.Pattern;
  40 
  41 import javax.annotation.processing.Filer;
  42 import javax.annotation.processing.RoundEnvironment;
  43 import javax.annotation.processing.SupportedAnnotationTypes;
  44 import javax.lang.model.SourceVersion;
  45 import javax.lang.model.element.AnnotationMirror;
  46 import javax.lang.model.element.Element;
  47 import javax.lang.model.element.ElementKind;
  48 import javax.lang.model.element.ExecutableElement;
  49 import javax.lang.model.element.Modifier;
  50 import javax.lang.model.element.Name;
  51 import javax.lang.model.element.PackageElement;
  52 import javax.lang.model.element.TypeElement;
  53 import javax.lang.model.element.VariableElement;
  54 import javax.lang.model.type.TypeMirror;
  55 import javax.lang.model.util.ElementFilter;
  56 import javax.lang.model.util.Types;
  57 import javax.tools.Diagnostic.Kind;
  58 import javax.tools.FileObject;
  59 import javax.tools.JavaFileObject;
  60 import javax.tools.StandardLocation;
  61 
  62 import org.graalvm.compiler.processor.AbstractProcessor;
  63 
  64 /**
  65  * Processes classes annotated with {@code MatchRule}. A {@code MatchStatementSet} service is
  66  * generated for each top level class containing at least one such field. These service objects can
  67  * be retrieved as follows:
  68  *
  69  * <pre>
  70  *     Iterable<MatchStatementSet> sl = GraalServices.load(MatchStatementSet.class);
  71  *     for (MatchStatementSet rules : sl) {
  72  *         ...
  73  *     }
  74  * </pre>
  75  */
  76 @SupportedAnnotationTypes({"org.graalvm.compiler.core.match.MatchRule", "org.graalvm.compiler.core.match.MatchRules", "org.graalvm.compiler.core.match.MatchableNode",
  77                 "org.graalvm.compiler.core.match.MatchableNodes"})
  78 public class MatchProcessor extends AbstractProcessor {
  79 
  80     private static final String VALUE_NODE_CLASS_NAME = "org.graalvm.compiler.nodes.ValueNode";
  81     private static final String COMPLEX_MATCH_RESULT_CLASS_NAME = "org.graalvm.compiler.core.match.ComplexMatchResult";
  82     private static final String MATCHABLE_NODES_CLASS_NAME = "org.graalvm.compiler.core.match.MatchableNodes";
  83     private static final String MATCHABLE_NODE_CLASS_NAME = "org.graalvm.compiler.core.match.MatchableNode";
  84     private static final String MATCH_RULE_CLASS_NAME = "org.graalvm.compiler.core.match.MatchRule";
  85     private static final String MATCH_RULES_CLASS_NAME = "org.graalvm.compiler.core.match.MatchRules";
  86 
  87     public MatchProcessor() {
  88     }
  89 
  90     @Override
  91     public SourceVersion getSupportedSourceVersion() {
  92         return SourceVersion.latest();
  93     }
  94 
  95     private final Set<Element> processedMatchRules = new HashSet<>();
  96     private final Set<Element> processedMatchableNodes = new HashSet<>();
  97 
  98     private static class RuleParseError extends RuntimeException {
  99         private static final long serialVersionUID = 6456128283609257490L;
 100 
 101         RuleParseError(String format, Object... args) {
 102             super(String.format(format, args));
 103         }
 104     }
 105 
 106     private static final Pattern tokenizer = Pattern.compile("\\s*([()=]|[A-Za-z][A-Za-z0-9]*)\\s*");
 107 
 108     private class RuleParser {
 109         private ArrayList<TypeDescriptor> capturedTypes = new ArrayList<>();
 110 
 111         private ArrayList<String> capturedNames = new ArrayList<>();
 112 
 113         private final String[] tokens;
 114 
 115         private int current;
 116 
 117         private MatchDescriptor matchDescriptor;
 118 
 119         private final Set<Element> originatingElements = new HashSet<>();
 120 
 121         private Set<String> requiredPackages = new HashSet<>();
 122 
 123         RuleParser(String rule) {
 124             Matcher m = tokenizer.matcher(rule);
 125             List<String> list = new ArrayList<>();
 126             int end = 0;
 127             while (m.lookingAt()) {
 128                 list.add(m.group(1));
 129                 end = m.end();
 130                 m.region(m.end(), m.regionEnd());
 131             }
 132             if (end != m.regionEnd()) {
 133                 throw new RuleParseError("Unexpected tokens :" + rule.substring(m.end(), m.regionEnd()));
 134             }
 135             tokens = list.toArray(new String[0]);
 136 
 137             matchDescriptor = parseExpression();
 138             if (!done()) {
 139                 throw new RuleParseError("didn't consume all tokens");
 140             }
 141             capturedNames.add(0, "root");
 142             capturedTypes.add(0, matchDescriptor.nodeType);
 143         }
 144 
 145         String next() {
 146             return tokens[current++];
 147         }
 148 
 149         String peek(String name) {
 150             if (current >= tokens.length) {
 151                 if (name == null) {
 152                     throw new RuleParseError("Out of tokens");
 153                 }
 154                 throw new RuleParseError("Out of tokens looking for %s", name);
 155             }
 156             return tokens[current];
 157         }
 158 
 159         boolean done() {
 160             return current == tokens.length;
 161         }
 162 
 163         private MatchDescriptor parseExpression() {
 164             if (peek("(").equals("(")) {
 165                 next();
 166                 MatchDescriptor descriptor = parseType(true);
 167                 for (int n = 0; n < descriptor.nodeType.inputs.size(); n++) {
 168                     if (peek("(").equals("(")) {
 169                         descriptor.inputs[n] = parseExpression();
 170                     } else {
 171                         descriptor.inputs[n] = parseType(false);
 172                     }
 173                 }
 174                 for (int n = 0; n < descriptor.nodeType.inputs.size(); n++) {
 175                     if (descriptor.inputs[n] == null) {
 176                         throw new RuleParseError("not enough inputs for " + descriptor.name);
 177                     }
 178                 }
 179                 if (peek(")").equals(")")) {
 180                     next();
 181                     return descriptor;
 182                 }
 183                 throw new RuleParseError("Too many arguments to " + descriptor.nodeType.nodeClass);
 184             }
 185             throw new RuleParseError("Extra tokens following match pattern: " + peek(null));
 186         }
 187 
 188         private MatchDescriptor parseType(boolean forExpression) {
 189             TypeDescriptor type = null;
 190             String name = null;
 191             if (Character.isUpperCase(peek("node type or name").charAt(0))) {
 192                 String token = next();
 193                 type = knownTypes.get(token);
 194                 if (type == null) {
 195                     throw new RuleParseError("Unknown node type: " + token);
 196                 }
 197                 if (peek("=").equals("=")) {
 198                     next();
 199                     name = next();
 200                 }
 201                 originatingElements.addAll(type.originatingElements);
 202             } else if (Character.isLowerCase(peek("name").charAt(0))) {
 203                 name = next();
 204                 type = valueType;
 205             } else {
 206                 throw new RuleParseError("Unexpected token \"%s\" when looking for name or node type", peek(null));
 207             }
 208             requiredPackages.add(type.nodePackage);
 209             if (name != null) {
 210                 if (!capturedNames.contains(name)) {
 211                     capturedNames.add(name);
 212                     capturedTypes.add(type);
 213                 } else {
 214                     int index = capturedNames.indexOf(name);
 215                     if (capturedTypes.get(index) != type) {
 216                         throw new RuleParseError("Captured node \"%s\" has differing types", name);
 217                     }
 218                 }
 219             }
 220             return new MatchDescriptor(type, name, forExpression);
 221         }
 222 
 223         List<String> generateVariants() {
 224             return matchDescriptor.generateVariants();
 225         }
 226 
 227         /**
 228          * Recursively accumulate any required Position declarations.
 229          */
 230         void generatePositionDeclarations(Set<String> declarations) {
 231             matchDescriptor.generatePositionDeclarations(declarations);
 232         }
 233 
 234         /**
 235          *
 236          * @return the list of node types which are captured by name
 237          */
 238         public ArrayList<TypeDescriptor> capturedTypes() {
 239             return capturedTypes;
 240         }
 241 
 242         public ArrayList<String> capturedNames() {
 243             return capturedNames;
 244         }
 245     }
 246 
 247     /**
 248      * Set to true to enable logging during annotation processing. There's no normal channel for any
 249      * debug messages and debugging annotation processors requires some special setup.
 250      */
 251     private static final boolean DEBUG = false;
 252 
 253     private PrintWriter log;
 254 
 255     /**
 256      * Logging facility for debugging the annotation processor.
 257      */
 258 
 259     private PrintWriter getLog() {
 260         if (log == null) {
 261             if (processingEnv.getClass().getName().contains(".javac.")) {
 262                 // For javac, just log to System.err
 263                 log = new PrintWriter(System.err);
 264             } else {
 265                 try {
 266                     // Create the log file within the generated source directory so it's easy to
 267                     // find.
 268                     // /tmp isn't platform independent and java.io.tmpdir can map anywhere,
 269                     // particularly
 270                     // on the mac.
 271                     FileObject file = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", getClass().getSimpleName() + "log");
 272                     log = new PrintWriter(new FileWriter(file.toUri().getPath(), true));
 273                 } catch (IOException e) {
 274                     // Do nothing
 275                 }
 276             }
 277         }
 278         return log;
 279     }
 280 
 281     private void logMessage(String format, Object... args) {
 282         if (!DEBUG) {
 283             return;
 284         }
 285         PrintWriter bw = getLog();
 286         if (bw != null) {
 287             bw.printf(format, args);
 288             bw.flush();
 289         }
 290     }
 291 
 292     private void logException(Throwable t) {
 293         if (!DEBUG) {
 294             return;
 295         }
 296         PrintWriter bw = getLog();
 297         if (bw != null) {
 298             t.printStackTrace(bw);
 299             bw.flush();
 300         }
 301     }
 302 
 303     /**
 304      * Bugs in an annotation processor can cause silent failure so try to report any exception
 305      * throws as errors.
 306      */
 307     private void reportExceptionThrow(Element element, Throwable t) {
 308         if (element != null) {
 309             logMessage("throw for %s:\n", element);
 310         }
 311         logException(t);
 312         printError(element, "Exception throw during processing: %s %s", t, Arrays.toString(Arrays.copyOf(t.getStackTrace(), 4)));
 313     }
 314 
 315     static class TypeDescriptor {
 316         final TypeMirror mirror;
 317 
 318         /**
 319          * The name uses in match expressions to refer to this type.
 320          */
 321         final String shortName;
 322 
 323         /**
 324          * The simple name of the {@code ValueNode} class represented by this type.
 325          */
 326         final String nodeClass;
 327 
 328         /**
 329          * The package of {@code ValueNode} class represented by this type.
 330          */
 331         final String nodePackage;
 332 
 333         /**
 334          * The matchable inputs of the node.
 335          */
 336         final List<String> inputs;
 337 
 338         /**
 339          * Should swapped variants of this match be generated. The user of the match is expected to
 340          * compensate for any ordering differences in compare which are commutative but require
 341          * reinterpreting the condition in that case.
 342          */
 343         final boolean commutative;
 344 
 345         /**
 346          * Can multiple users of this node subsume it. Constants can be swallowed into a match even
 347          * if there are multiple users.
 348          */
 349         final boolean shareable;
 350 
 351         final Set<Element> originatingElements = new HashSet<>();
 352 
 353         TypeDescriptor(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, List<String> inputs, boolean commutative, boolean shareable) {
 354             this.mirror = mirror;
 355             this.shortName = shortName;
 356             this.nodeClass = nodeClass;
 357             this.nodePackage = nodePackage;
 358             this.inputs = inputs;
 359             this.commutative = commutative;
 360             this.shareable = shareable;
 361             assert !commutative || inputs.size() == 2;
 362         }
 363     }
 364 
 365     /**
 366      * The types which are know for purpose of parsing MatchRule expressions.
 367      */
 368     Map<String, TypeDescriptor> knownTypes = new HashMap<>();
 369 
 370     private TypeDescriptor valueType;
 371 
 372     private void declareType(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, List<String> inputs, boolean commutative, boolean shareable, Element element) {
 373         TypeDescriptor descriptor = new TypeDescriptor(mirror, shortName, nodeClass, nodePackage, inputs, commutative, shareable);
 374         descriptor.originatingElements.add(element);
 375         knownTypes.put(shortName, descriptor);
 376     }
 377 
 378     private String findPackage(Element type) {
 379         PackageElement p = processingEnv.getElementUtils().getPackageOf(type);
 380         if (p != null) {
 381             return p.getQualifiedName().toString();
 382         }
 383         throw new InternalError("Can't find package for " + type);
 384     }
 385 
 386     class MatchDescriptor {
 387         TypeDescriptor nodeType;
 388         String name;
 389         MatchDescriptor[] inputs;
 390 
 391         MatchDescriptor(TypeDescriptor nodeType, String name, boolean forExpression) {
 392             this.nodeType = nodeType;
 393             this.name = name;
 394             if (forExpression) {
 395                 this.inputs = new MatchDescriptor[nodeType.inputs.size()];
 396             } else {
 397                 this.inputs = new MatchDescriptor[0];
 398             }
 399         }
 400 
 401         public void generatePositionDeclarations(Set<String> declarations) {
 402             if (inputs.length == 0) {
 403                 return;
 404             }
 405             declarations.add(generatePositionDeclaration());
 406             for (MatchDescriptor desc : inputs) {
 407                 desc.generatePositionDeclarations(declarations);
 408             }
 409         }
 410 
 411         List<String> recurseVariants(int index) {
 412             if (inputs.length == 0) {
 413                 return new ArrayList<>();
 414             }
 415             List<String> currentVariants = inputs[index].generateVariants();
 416             if (index == inputs.length - 1) {
 417                 return currentVariants;
 418             }
 419             List<String> subVariants = recurseVariants(index + 1);
 420             List<String> result = new ArrayList<>();
 421             for (String current : currentVariants) {
 422                 for (String sub : subVariants) {
 423                     result.add(current + ", " + sub);
 424                     if (nodeType.commutative) {
 425                         result.add(sub + ", " + current);
 426                     }
 427                 }
 428             }
 429             return result;
 430         }
 431 
 432         /**
 433          * Recursively generate all the variants of this rule pattern. Currently that just means to
 434          * swap the inputs for commutative rules, producing all possible permutations.
 435          *
 436          * @return a list of Strings which will construct pattern matchers for this rule.
 437          */
 438         List<String> generateVariants() {
 439             String prefix = formatPrefix();
 440             String suffix = formatSuffix();
 441             ArrayList<String> variants = new ArrayList<>();
 442             if (inputs.length > 0) {
 443                 for (String var : recurseVariants(0)) {
 444                     variants.add(prefix + ", " + var + suffix);
 445                 }
 446             } else {
 447                 assert inputs.length == 0;
 448                 variants.add(prefix + suffix);
 449             }
 450 
 451             return variants;
 452         }
 453 
 454         private String formatPrefix() {
 455             if (nodeType == valueType) {
 456                 return String.format("new MatchPattern(%s, false", name != null ? ("\"" + name + "\"") : "null");
 457             } else {
 458                 return String.format("new MatchPattern(%s.class, %s", nodeType.nodeClass, name != null ? ("\"" + name + "\"") : "null");
 459             }
 460         }
 461 
 462         private String formatSuffix() {
 463             if (nodeType != null) {
 464                 if (inputs.length != nodeType.inputs.size()) {
 465                     return ", true)";
 466                 } else {
 467                     if (nodeType.inputs.size() > 0) {
 468                         return ", " + nodeType.nodeClass + "_positions, " + !nodeType.shareable + ")";
 469                     }
 470                     if (nodeType.shareable) {
 471                         return ", false)";
 472                     }
 473                 }
 474             }
 475             return ")";
 476         }
 477 
 478         String generatePositionDeclaration() {
 479             return String.format("Position[] %s_positions = MatchRuleRegistry.findPositions(%s.TYPE, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass,
 480                             String.join("\", \"", nodeType.inputs));
 481         }
 482     }
 483 
 484     /**
 485      * Strip the package off a class name leaving the full class name including any outer classes.
 486      */
 487     private String fullClassName(Element element) {
 488         assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE : element;
 489         String pkg = findPackage(element);
 490         return ((TypeElement) element).getQualifiedName().toString().substring(pkg.length() + 1);
 491     }
 492 
 493     private void createFiles(MatchRuleDescriptor info) {
 494         String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString();
 495         Name topDeclaringClass = info.topDeclaringType.getSimpleName();
 496 
 497         String matchStatementClassName = topDeclaringClass + "_MatchStatementSet";
 498         Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]);
 499 
 500         Types typeUtils = typeUtils();
 501         Filer filer = processingEnv.getFiler();
 502         try (PrintWriter out = createSourceFile(pkg, matchStatementClassName, filer, originatingElements)) {
 503 
 504             out.println("// CheckStyle: stop header check");
 505             out.println("// CheckStyle: stop line length check");
 506             out.println("// GENERATED CONTENT - DO NOT EDIT");
 507             out.println("// Source: " + topDeclaringClass + ".java");
 508             out.println("package " + pkg + ";");
 509             out.println("");
 510             out.println("import java.util.*;");
 511             out.println("import org.graalvm.compiler.core.match.*;");
 512             out.println("import org.graalvm.compiler.core.gen.NodeMatchRules;");
 513             out.println("import org.graalvm.compiler.graph.Position;");
 514             for (String p : info.requiredPackages) {
 515                 out.println("import " + p + ".*;");
 516             }
 517             out.println("");
 518 
 519             out.println("public class " + matchStatementClassName + " implements MatchStatementSet {");
 520 
 521             out.println();
 522 
 523             // Generate declarations for the wrapper class to invoke the code generation methods.
 524             for (MethodInvokerItem invoker : info.invokers.values()) {
 525                 StringBuilder args = new StringBuilder();
 526                 StringBuilder types = new StringBuilder();
 527                 int count = invoker.fields.size();
 528                 int index = 0;
 529                 for (VariableElement arg : invoker.fields) {
 530                     args.append('"');
 531                     args.append(arg.getSimpleName());
 532                     args.append('"');
 533                     types.append(String.format("(%s) args[%s]", fullClassName(typeUtils.asElement(arg.asType())), index++));
 534                     if (count-- > 1) {
 535                         args.append(", ");
 536                         types.append(", ");
 537                     }
 538                 }
 539                 out.printf("    private static final String[] %s = new String[] {%s};\n", invoker.argumentsListName(), args);
 540                 out.printf("    private static final class %s implements MatchGenerator {\n", invoker.wrapperClass());
 541                 out.printf("        static MatchGenerator instance = new %s();\n", invoker.wrapperClass());
 542                 out.printf("        @Override\n");
 543                 out.printf("        public ComplexMatchResult match(NodeMatchRules nodeMatchRules, Object...args) {\n");
 544                 out.printf("            return ((%s) nodeMatchRules).%s(%s);\n", invoker.nodeLIRBuilderClass, invoker.methodName, types);
 545                 out.printf("        }\n");
 546                 out.printf("        @Override\n");
 547                 out.printf("        public String getName() {\n");
 548                 out.printf("             return \"%s\";\n", invoker.methodName);
 549                 out.printf("        }\n");
 550                 out.printf("    }\n");
 551                 out.println();
 552 
 553             }
 554 
 555             String desc = "MatchStatement";
 556 
 557             out.println("    @Override");
 558             out.println("    public Class<? extends NodeMatchRules> forClass() {");
 559             out.println("        return " + topDeclaringClass + ".class;");
 560             out.println("    }");
 561             out.println();
 562             out.println("    @Override");
 563             out.println("    public List<" + desc + "> statements() {");
 564             out.println("        // Checkstyle: stop ");
 565 
 566             for (String positionDeclaration : info.positionDeclarations) {
 567                 out.println("        " + positionDeclaration);
 568             }
 569             out.println();
 570 
 571             out.println("        List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList(");
 572 
 573             int i = 0;
 574             for (MatchRuleItem matchRule : info.matchRules) {
 575                 String comma = i == info.matchRules.size() - 1 ? "" : ",";
 576                 out.printf("            %s%s\n", matchRule.ruleBuilder(), comma);
 577                 i++;
 578             }
 579             out.println("        ));");
 580             out.println("        // Checkstyle: resume");
 581             out.println("        return statements;");
 582             out.println("    }");
 583 
 584             out.println();
 585 
 586             out.println("}");
 587         }
 588         this.createProviderFile(pkg + "." + matchStatementClassName, "org.graalvm.compiler.core.match.MatchStatementSet", originatingElements);
 589     }
 590 
 591     protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
 592         try {
 593             // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle
 594             JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements);
 595             return new PrintWriter(sourceFile.openWriter()) {
 596 
 597                 @Override
 598                 public void println() {
 599                     print("\n");
 600                 }
 601             };
 602         } catch (IOException e) {
 603             throw new RuntimeException(e);
 604         }
 605     }
 606 
 607     /**
 608      * Used to generate the MatchStatement constructor invocation.
 609      */
 610     static class MatchRuleItem {
 611         private final String matchPattern;
 612         private final MethodInvokerItem invoker;
 613 
 614         MatchRuleItem(String matchPattern, MethodInvokerItem invoker) {
 615             this.matchPattern = matchPattern;
 616             this.invoker = invoker;
 617         }
 618 
 619         /**
 620          * @return a string which will construct the MatchStatement instance to match this pattern.
 621          */
 622         public String ruleBuilder() {
 623             return String.format("new MatchStatement(\"%s\", %s, %s.instance, %s)", invoker.methodName, matchPattern, invoker.wrapperClass(), invoker.argumentsListName());
 624         }
 625     }
 626 
 627     /**
 628      * Used to generate the wrapper class to invoke the code generation method.
 629      */
 630     static class MethodInvokerItem {
 631         final String methodName;
 632         final String nodeLIRBuilderClass;
 633         final ExecutableElement method;
 634         final List<? extends VariableElement> fields;
 635 
 636         MethodInvokerItem(String methodName, String nodeLIRBuilderClass, ExecutableElement method, List<? extends VariableElement> fields) {
 637             this.methodName = methodName;
 638             this.nodeLIRBuilderClass = nodeLIRBuilderClass;
 639             this.method = method;
 640             this.fields = fields;
 641         }
 642 
 643         String wrapperClass() {
 644             return "MatchGenerator_" + methodName;
 645         }
 646 
 647         String argumentsListName() {
 648             return methodName + "_arguments";
 649         }
 650     }
 651 
 652     static class MatchRuleDescriptor {
 653 
 654         final TypeElement topDeclaringType;
 655         final List<MatchRuleItem> matchRules = new ArrayList<>();
 656         private final Set<Element> originatingElements = new HashSet<>();
 657         public Set<String> positionDeclarations = new HashSet<>();
 658 
 659         /**
 660          * The mapping between elements with MatchRules and the wrapper class used invoke the code
 661          * generation after the match.
 662          */
 663         Map<String, MethodInvokerItem> invokers = new HashMap<>();
 664 
 665         /**
 666          * The set of packages which must be imported to refer the classes mentioned in matchRules.
 667          */
 668         Set<String> requiredPackages = new HashSet<>();
 669 
 670         MatchRuleDescriptor(TypeElement topDeclaringType) {
 671             this.topDeclaringType = topDeclaringType;
 672         }
 673     }
 674 
 675     private static TypeElement topDeclaringType(Element element) {
 676         Element enclosing = element.getEnclosingElement();
 677         if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) {
 678             assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE;
 679             return (TypeElement) element;
 680         }
 681         return topDeclaringType(enclosing);
 682     }
 683 
 684     /**
 685      * The element currently being processed.
 686      */
 687     private Element currentElement;
 688 
 689     /**
 690      * The current processing round.
 691      */
 692     private RoundEnvironment currentRound;
 693 
 694     @Override
 695     public boolean doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 696         if (roundEnv.processingOver()) {
 697             return true;
 698         }
 699 
 700         logMessage("Starting round %s\n", roundEnv);
 701 
 702         TypeElement matchRulesTypeElement = getTypeElement(MATCH_RULES_CLASS_NAME);
 703         TypeElement matchRuleTypeElement = getTypeElement(MATCH_RULE_CLASS_NAME);
 704 
 705         TypeMirror matchRulesTypeMirror = matchRulesTypeElement.asType();
 706         TypeMirror matchRuleTypeMirror = matchRuleTypeElement.asType();
 707 
 708         TypeElement matchableNodeTypeElement = getTypeElement(MATCHABLE_NODE_CLASS_NAME);
 709         TypeElement matchableNodesTypeElement = getTypeElement(MATCHABLE_NODES_CLASS_NAME);
 710 
 711         currentRound = roundEnv;
 712         try {
 713             for (Element element : roundEnv.getElementsAnnotatedWith(matchableNodeTypeElement)) {
 714                 currentElement = element;
 715                 logMessage("%s\n", element);
 716                 processMatchableNodes(element);
 717             }
 718             for (Element element : roundEnv.getElementsAnnotatedWith(matchableNodesTypeElement)) {
 719                 currentElement = element;
 720                 logMessage("%s\n", element);
 721                 processMatchableNodes(element);
 722             }
 723             // Define a TypeDescriptor for the generic node but don't enter it into the nodeTypes
 724             // table since it shouldn't be mentioned in match rules.
 725             TypeMirror valueTypeMirror = getTypeElement(VALUE_NODE_CLASS_NAME).asType();
 726             valueType = new TypeDescriptor(valueTypeMirror, "Value", "ValueNode", "org.graalvm.compiler.nodes", Collections.emptyList(), false, false);
 727 
 728             Map<TypeElement, MatchRuleDescriptor> map = new HashMap<>();
 729 
 730             for (Element element : roundEnv.getElementsAnnotatedWith(matchRuleTypeElement)) {
 731                 currentElement = element;
 732                 AnnotationMirror matchRule = getAnnotation(element, matchRuleTypeMirror);
 733                 List<AnnotationMirror> matchRuleAnnotations = Collections.singletonList(matchRule);
 734                 processMatchRules(map, element, matchRuleAnnotations);
 735             }
 736             for (Element element : roundEnv.getElementsAnnotatedWith(matchRulesTypeElement)) {
 737                 currentElement = element;
 738                 AnnotationMirror matchRules = getAnnotation(element, matchRulesTypeMirror);
 739                 List<AnnotationMirror> matchRuleAnnotations = getAnnotationValueList(matchRules, "value", AnnotationMirror.class);
 740                 processMatchRules(map, element, matchRuleAnnotations);
 741             }
 742 
 743             currentElement = null;
 744             for (MatchRuleDescriptor info : map.values()) {
 745                 createFiles(info);
 746             }
 747 
 748         } catch (Throwable t) {
 749             reportExceptionThrow(currentElement, t);
 750         } finally {
 751             currentElement = null;
 752             currentRound = null;
 753         }
 754 
 755         return true;
 756     }
 757 
 758     /**
 759      * Build up the type table to be used during parsing of the MatchRule.
 760      */
 761     private void processMatchableNodes(Element element) {
 762         if (!processedMatchableNodes.contains(element)) {
 763             try {
 764                 processedMatchableNodes.add(element);
 765 
 766                 List<AnnotationMirror> matchableNodeAnnotations;
 767                 AnnotationMirror mirror = getAnnotation(element, getType(MATCHABLE_NODES_CLASS_NAME));
 768                 if (mirror != null) {
 769                     matchableNodeAnnotations = getAnnotationValueList(mirror, "value", AnnotationMirror.class);
 770                 } else {
 771                     mirror = getAnnotation(element, getType(MATCHABLE_NODES_CLASS_NAME));
 772                     if (mirror != null) {
 773                         matchableNodeAnnotations = Collections.singletonList(mirror);
 774                     } else {
 775                         return;
 776                     }
 777                 }
 778 
 779                 TypeElement topDeclaringType = topDeclaringType(element);
 780                 for (AnnotationMirror matchableNode : matchableNodeAnnotations) {
 781                     processMatchableNode(element, topDeclaringType, matchableNode);
 782                 }
 783             } catch (Throwable t) {
 784                 reportExceptionThrow(element, t);
 785             }
 786         }
 787     }
 788 
 789     private void processMatchableNode(Element element, TypeElement topDeclaringType, AnnotationMirror matchable) {
 790         logMessage("processMatchableNode %s %s %s\n", topDeclaringType, element, matchable);
 791         String nodeClass;
 792         String nodePackage;
 793         TypeMirror nodeClassMirror = getAnnotationValue(matchable, "nodeClass", TypeMirror.class);
 794         if (nodeClassMirror == null) {
 795             throw new InternalError("Can't get mirror for node class " + element);
 796         }
 797         if (nodeClassMirror.toString().equals(MATCHABLE_NODE_CLASS_NAME)) {
 798             nodeClass = topDeclaringType.getQualifiedName().toString();
 799         } else {
 800             nodeClass = nodeClassMirror.toString();
 801         }
 802         TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(nodeClass);
 803         if (typeElement == null) {
 804             printError(element, matchable, "Class \"%s\" cannot be resolved to a type", nodeClass);
 805             return;
 806         }
 807         nodePackage = findPackage(typeElement);
 808         assert nodeClass.startsWith(nodePackage);
 809         nodeClass = nodeClass.substring(nodePackage.length() + 1);
 810         assert nodeClass.endsWith("Node");
 811         String shortName = nodeClass.substring(0, nodeClass.length() - 4);
 812 
 813         Types typeUtils = processingEnv.getTypeUtils();
 814         TypeElement nodeClassElement = (TypeElement) typeUtils.asElement(nodeClassMirror);
 815         List<String> inputs = getAnnotationValueList(matchable, "inputs", String.class);
 816         for (String input : inputs) {
 817             boolean ok = false;
 818             TypeElement current = nodeClassElement;
 819             while (!ok && current != null) {
 820                 for (Element fieldElement : ElementFilter.fieldsIn(current.getEnclosedElements())) {
 821                     if (fieldElement.getSimpleName().toString().equals(input)) {
 822                         ok = true;
 823                         break;
 824                     }
 825                 }
 826                 TypeMirror theSuper = current.getSuperclass();
 827                 current = (TypeElement) typeUtils.asElement(theSuper);
 828             }
 829             if (!ok) {
 830                 printError(element, matchable, "Input named \"%s\" doesn't exist in %s", input, nodeClassElement.getSimpleName());
 831             }
 832         }
 833 
 834         boolean commutative = getAnnotationValue(matchable, "commutative", Boolean.class);
 835         boolean shareable = getAnnotationValue(matchable, "shareable", Boolean.class);
 836         declareType(nodeClassMirror, shortName, nodeClass, nodePackage, inputs, commutative, shareable, element);
 837     }
 838 
 839     private void processMatchRules(Map<TypeElement, MatchRuleDescriptor> map, Element element, List<AnnotationMirror> matchRules) {
 840         if (!processedMatchRules.contains(element)) {
 841             try {
 842                 processedMatchRules.add(element);
 843 
 844                 // The annotation element type should ensure this is true.
 845                 assert element instanceof ExecutableElement;
 846 
 847                 findMatchableNodes(element);
 848 
 849                 TypeElement topDeclaringType = topDeclaringType(element);
 850                 MatchRuleDescriptor info = map.get(topDeclaringType);
 851                 if (info == null) {
 852                     info = new MatchRuleDescriptor(topDeclaringType);
 853                     map.put(topDeclaringType, info);
 854                 }
 855                 for (AnnotationMirror matchRule : matchRules) {
 856                     processMatchRule((ExecutableElement) element, info, matchRule);
 857                 }
 858             } catch (Throwable t) {
 859                 reportExceptionThrow(element, t);
 860             }
 861         }
 862     }
 863 
 864     /**
 865      * Search the super types of element for MatchableNode definitions. Any superclass or super
 866      * interface can contain definitions of matchable nodes.
 867      *
 868      * @param element
 869      */
 870     private void findMatchableNodes(Element element) {
 871         processMatchableNodes(element);
 872         Element enclosing = element.getEnclosingElement();
 873         while (enclosing != null) {
 874             if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
 875                 TypeElement current = (TypeElement) enclosing;
 876                 while (current != null) {
 877                     processMatchableNodes(current);
 878                     for (TypeMirror intf : current.getInterfaces()) {
 879                         Element interfaceElement = typeUtils().asElement(intf);
 880                         processMatchableNodes(interfaceElement);
 881                         // Recurse
 882                         findMatchableNodes(interfaceElement);
 883                     }
 884                     TypeMirror theSuper = current.getSuperclass();
 885                     current = (TypeElement) typeUtils().asElement(theSuper);
 886                 }
 887             }
 888             enclosing = enclosing.getEnclosingElement();
 889         }
 890     }
 891 
 892     private Types typeUtils() {
 893         return processingEnv.getTypeUtils();
 894     }
 895 
 896     private void processMatchRule(ExecutableElement method, MatchRuleDescriptor info, AnnotationMirror matchRule) {
 897         logMessage("processMatchRule %s\n", method);
 898 
 899         Types typeUtils = typeUtils();
 900 
 901         if (!method.getModifiers().contains(Modifier.PUBLIC)) {
 902             printError(method, "MatchRule method %s must be public", method.getSimpleName());
 903             return;
 904         }
 905         if (method.getModifiers().contains(Modifier.STATIC)) {
 906             printError(method, "MatchRule method %s must be non-static", method.getSimpleName());
 907             return;
 908         }
 909 
 910         try {
 911             TypeMirror returnType = method.getReturnType();
 912             if (!typeUtils.isSameType(returnType, processingEnv.getElementUtils().getTypeElement(COMPLEX_MATCH_RESULT_CLASS_NAME).asType())) {
 913                 printError(method, "MatchRule method return type must be %s", COMPLEX_MATCH_RESULT_CLASS_NAME);
 914                 return;
 915             }
 916 
 917             String rule = getAnnotationValue(matchRule, "value", String.class);
 918             RuleParser parser = new RuleParser(rule);
 919             ArrayList<TypeDescriptor> expectedTypes = parser.capturedTypes();
 920             ArrayList<String> expectedNames = parser.capturedNames();
 921             List<? extends VariableElement> actualParameters = method.getParameters();
 922             if (expectedTypes.size() + 1 < actualParameters.size()) {
 923                 printError(method, "Too many arguments for match method %s != %s", expectedTypes.size() + 1, actualParameters.size());
 924                 return;
 925             }
 926 
 927             // Walk through the parameters to the method and see if they exist in the match rule.
 928             // The order doesn't matter but only names mentioned in the rule can be used and they
 929             // must be assignment compatible.
 930             for (VariableElement parameter : actualParameters) {
 931                 String name = parameter.getSimpleName().toString();
 932                 int nameIndex = expectedNames.indexOf(name);
 933                 if (nameIndex == -1) {
 934                     printError(method, "Argument \"%s\" isn't captured in the match rule", name);
 935                     return;
 936                 }
 937                 TypeMirror type = parameter.asType();
 938                 if (!typeUtils.isAssignable(expectedTypes.get(nameIndex).mirror, type)) {
 939                     printError(method, "Captured value \"%s\" of type %s is not assignable to argument of type %s", name, expectedTypes.get(nameIndex).mirror, type);
 940                     return;
 941                 }
 942             }
 943 
 944             String methodName = method.getSimpleName().toString();
 945             MethodInvokerItem invoker = info.invokers.get(methodName);
 946             if (invoker == null) {
 947                 invoker = new MethodInvokerItem(methodName, topDeclaringType(method).getSimpleName().toString(), method, actualParameters);
 948                 info.invokers.put(methodName, invoker);
 949             } else if (invoker.method != method) {
 950                 // This could be supported but it's easier if they are unique since the names
 951                 // are used in log output and snippet counters.
 952                 printError(method, "Use unique method names for match methods: %s.%s != %s.%s", method.getReceiverType(), method.getSimpleName(), invoker.method.getReceiverType(),
 953                                 invoker.method.getSimpleName());
 954                 return;
 955             }
 956 
 957             Element enclosing = method.getEnclosingElement();
 958             String declaringClass = "";
 959             String separator = "";
 960             Set<Element> originatingElementsList = info.originatingElements;
 961             originatingElementsList.add(method);
 962             while (enclosing != null) {
 963                 if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
 964                     if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
 965                         printError(method, "MatchRule cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
 966                         return;
 967                     }
 968                     originatingElementsList.add(enclosing);
 969                     declaringClass = enclosing.getSimpleName() + separator + declaringClass;
 970                     separator = ".";
 971                 } else {
 972                     assert enclosing.getKind() == ElementKind.PACKAGE;
 973                 }
 974                 enclosing = enclosing.getEnclosingElement();
 975             }
 976 
 977             originatingElementsList.addAll(parser.originatingElements);
 978             info.requiredPackages.addAll(parser.requiredPackages);
 979 
 980             // Accumulate any position declarations.
 981             parser.generatePositionDeclarations(info.positionDeclarations);
 982 
 983             List<String> matches = parser.generateVariants();
 984             for (String match : matches) {
 985                 info.matchRules.add(new MatchRuleItem(match, invoker));
 986             }
 987         } catch (RuleParseError e) {
 988             printError(method, matchRule, e.getMessage());
 989         }
 990     }
 991 
 992     private Element elementForMessage(Element e) {
 993         if (currentRound != null && !currentRound.getRootElements().contains(e) && currentElement != null) {
 994             return currentElement;
 995         }
 996         return e;
 997     }
 998 
 999     private void printError(Element annotatedElement, String format, Object... args) {
1000         Element e = elementForMessage(annotatedElement);
1001         String prefix = e == annotatedElement ? "" : annotatedElement + ": ";
1002         processingEnv.getMessager().printMessage(Kind.ERROR, prefix + String.format(format, args), e);
1003     }
1004 
1005     private void printError(Element annotatedElement, AnnotationMirror annotation, String format, Object... args) {
1006         Element e = elementForMessage(annotatedElement);
1007         String prefix = e == annotatedElement ? "" : annotation + " on " + annotatedElement + ": ";
1008         processingEnv.getMessager().printMessage(Kind.ERROR, prefix + String.format(format, args), e, annotation);
1009     }
1010 }