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         /**
 352          * Can this node be subsumed into a match even if there are side effecting nodes between
 353          * this node and the match.
 354          */
 355         final boolean ignoresSideEffects;
 356 
 357         final Set<Element> originatingElements = new HashSet<>();
 358 
 359         TypeDescriptor(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, List<String> inputs, boolean commutative, boolean shareable, boolean ignoresSideEffects) {
 360             this.mirror = mirror;
 361             this.shortName = shortName;
 362             this.nodeClass = nodeClass;
 363             this.nodePackage = nodePackage;
 364             this.inputs = inputs;
 365             this.commutative = commutative;
 366             this.shareable = shareable;
 367             this.ignoresSideEffects = ignoresSideEffects;
 368             assert !commutative || inputs.size() == 2;
 369         }
 370     }
 371 
 372     /**
 373      * The types which are know for purpose of parsing MatchRule expressions.
 374      */
 375     Map<String, TypeDescriptor> knownTypes = new HashMap<>();
 376 
 377     private TypeDescriptor valueType;
 378 
 379     private void declareType(TypeMirror mirror, String shortName, String nodeClass, String nodePackage, List<String> inputs, boolean commutative, boolean shareable, boolean ignoresSideEffects,
 380                     Element element) {
 381         TypeDescriptor descriptor = new TypeDescriptor(mirror, shortName, nodeClass, nodePackage, inputs, commutative, shareable, ignoresSideEffects);
 382         descriptor.originatingElements.add(element);
 383         knownTypes.put(shortName, descriptor);
 384     }
 385 
 386     private String findPackage(Element type) {
 387         PackageElement p = processingEnv.getElementUtils().getPackageOf(type);
 388         if (p != null) {
 389             return p.getQualifiedName().toString();
 390         }
 391         throw new InternalError("Can't find package for " + type);
 392     }
 393 
 394     class MatchDescriptor {
 395         TypeDescriptor nodeType;
 396         String name;
 397         MatchDescriptor[] inputs;
 398 
 399         MatchDescriptor(TypeDescriptor nodeType, String name, boolean forExpression) {
 400             this.nodeType = nodeType;
 401             this.name = name;
 402             if (forExpression) {
 403                 this.inputs = new MatchDescriptor[nodeType.inputs.size()];
 404             } else {
 405                 this.inputs = new MatchDescriptor[0];
 406             }
 407         }
 408 
 409         public void generatePositionDeclarations(Set<String> declarations) {
 410             if (inputs.length == 0) {
 411                 return;
 412             }
 413             declarations.add(generatePositionDeclaration());
 414             for (MatchDescriptor desc : inputs) {
 415                 desc.generatePositionDeclarations(declarations);
 416             }
 417         }
 418 
 419         List<String> recurseVariants(int index) {
 420             if (inputs.length == 0) {
 421                 return new ArrayList<>();
 422             }
 423             List<String> currentVariants = inputs[index].generateVariants();
 424             if (index == inputs.length - 1) {
 425                 return currentVariants;
 426             }
 427             List<String> subVariants = recurseVariants(index + 1);
 428             List<String> result = new ArrayList<>();
 429             for (String current : currentVariants) {
 430                 for (String sub : subVariants) {
 431                     result.add(current + ", " + sub);
 432                     if (nodeType.commutative) {
 433                         result.add(sub + ", " + current);
 434                     }
 435                 }
 436             }
 437             return result;
 438         }
 439 
 440         /**
 441          * Recursively generate all the variants of this rule pattern. Currently that just means to
 442          * swap the inputs for commutative rules, producing all possible permutations.
 443          *
 444          * @return a list of Strings which will construct pattern matchers for this rule.
 445          */
 446         List<String> generateVariants() {
 447             String prefix = formatPrefix();
 448             String suffix = formatSuffix();
 449             ArrayList<String> variants = new ArrayList<>();
 450             if (inputs.length > 0) {
 451                 for (String var : recurseVariants(0)) {
 452                     variants.add(prefix + ", " + var + suffix);
 453                 }
 454             } else {
 455                 assert inputs.length == 0;
 456                 variants.add(prefix + suffix);
 457             }
 458 
 459             return variants;
 460         }
 461 
 462         private String formatPrefix() {
 463             if (nodeType == valueType) {
 464                 return String.format("new MatchPattern(%s, false, false", name != null ? ("\"" + name + "\"") : "null");
 465             } else {
 466                 return String.format("new MatchPattern(%s.class, %s", nodeType.nodeClass, name != null ? ("\"" + name + "\"") : "null");
 467             }
 468         }
 469 
 470         private String formatSuffix() {
 471             if (nodeType != null) {
 472                 if (inputs.length != nodeType.inputs.size()) {
 473                     return ", true, " + nodeType.ignoresSideEffects + ")";
 474                 } else {
 475                     if (nodeType.inputs.size() > 0) {
 476                         return ", " + nodeType.nodeClass + "_positions, " + !nodeType.shareable + ", " + nodeType.ignoresSideEffects + ")";
 477                     }
 478                     if (nodeType.shareable) {
 479                         return ", false, " + nodeType.ignoresSideEffects + ")";
 480                     }
 481                 }
 482             }
 483             return ")";
 484         }
 485 
 486         String generatePositionDeclaration() {
 487             return String.format("Position[] %s_positions = MatchRuleRegistry.findPositions(%s.TYPE, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass,
 488                             String.join("\", \"", nodeType.inputs));
 489         }
 490     }
 491 
 492     /**
 493      * Strip the package off a class name leaving the full class name including any outer classes.
 494      */
 495     private String fullClassName(Element element) {
 496         String pkg = findPackage(element);
 497         return ((TypeElement) element).getQualifiedName().toString().substring(pkg.length() + 1);
 498     }
 499 
 500     private void createFiles(MatchRuleDescriptor info) {
 501         String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString();
 502         Name topDeclaringClass = info.topDeclaringType.getSimpleName();
 503 
 504         String matchStatementClassName = topDeclaringClass + "_MatchStatementSet";
 505         Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]);
 506 
 507         Types typeUtils = typeUtils();
 508         Filer filer = processingEnv.getFiler();
 509         try (PrintWriter out = createSourceFile(pkg, matchStatementClassName, filer, originatingElements)) {
 510 
 511             out.println("// CheckStyle: stop header check");
 512             out.println("// CheckStyle: stop line length check");
 513             out.println("// GENERATED CONTENT - DO NOT EDIT");
 514             out.println("// Source: " + topDeclaringClass + ".java");
 515             out.println("package " + pkg + ";");
 516             out.println("");
 517             out.println("import java.util.*;");
 518             out.println("import org.graalvm.compiler.core.match.*;");
 519             out.println("import org.graalvm.compiler.core.gen.NodeMatchRules;");
 520             out.println("import org.graalvm.compiler.graph.Position;");
 521             for (String p : info.requiredPackages) {
 522                 out.println("import " + p + ".*;");
 523             }
 524             out.println("");
 525 
 526             out.println("public class " + matchStatementClassName + " implements MatchStatementSet {");
 527 
 528             out.println();
 529 
 530             // Generate declarations for the wrapper class to invoke the code generation methods.
 531             for (MethodInvokerItem invoker : info.invokers.values()) {
 532                 StringBuilder args = new StringBuilder();
 533                 StringBuilder types = new StringBuilder();
 534                 int count = invoker.fields.size();
 535                 int index = 0;
 536                 for (VariableElement arg : invoker.fields) {
 537                     args.append('"');
 538                     args.append(arg.getSimpleName());
 539                     args.append('"');
 540                     types.append(String.format("(%s) args[%s]", fullClassName(typeUtils.asElement(arg.asType())), index++));
 541                     if (count-- > 1) {
 542                         args.append(", ");
 543                         types.append(", ");
 544                     }
 545                 }
 546                 out.printf("    private static final String[] %s = new String[] {%s};\n", invoker.argumentsListName(), args);
 547                 out.printf("    private static final class %s implements MatchGenerator {\n", invoker.wrapperClass());
 548                 out.printf("        static MatchGenerator instance = new %s();\n", invoker.wrapperClass());
 549                 out.printf("        @Override\n");
 550                 out.printf("        public ComplexMatchResult match(NodeMatchRules nodeMatchRules, Object...args) {\n");
 551                 out.printf("            return ((%s) nodeMatchRules).%s(%s);\n", invoker.nodeLIRBuilderClass, invoker.methodName, types);
 552                 out.printf("        }\n");
 553                 out.printf("        @Override\n");
 554                 out.printf("        public String getName() {\n");
 555                 out.printf("             return \"%s\";\n", invoker.methodName);
 556                 out.printf("        }\n");
 557                 out.printf("    }\n");
 558                 out.println();
 559 
 560             }
 561 
 562             String desc = "MatchStatement";
 563 
 564             out.println("    @Override");
 565             out.println("    public Class<? extends NodeMatchRules> forClass() {");
 566             out.println("        return " + topDeclaringClass + ".class;");
 567             out.println("    }");
 568             out.println();
 569             out.println("    @Override");
 570             out.println("    public List<" + desc + "> statements() {");
 571             out.println("        // Checkstyle: stop ");
 572 
 573             for (String positionDeclaration : info.positionDeclarations) {
 574                 out.println("        " + positionDeclaration);
 575             }
 576             out.println();
 577 
 578             out.println("        List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList(");
 579 
 580             int i = 0;
 581             for (MatchRuleItem matchRule : info.matchRules) {
 582                 String comma = i == info.matchRules.size() - 1 ? "" : ",";
 583                 out.printf("            %s%s\n", matchRule.ruleBuilder(), comma);
 584                 i++;
 585             }
 586             out.println("        ));");
 587             out.println("        // Checkstyle: resume");
 588             out.println("        return statements;");
 589             out.println("    }");
 590 
 591             out.println();
 592 
 593             out.println("}");
 594         }
 595         this.createProviderFile(pkg + "." + matchStatementClassName, "org.graalvm.compiler.core.match.MatchStatementSet", originatingElements);
 596     }
 597 
 598     protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
 599         try {
 600             // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle
 601             JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements);
 602             return new PrintWriter(sourceFile.openWriter()) {
 603 
 604                 @Override
 605                 public void println() {
 606                     print("\n");
 607                 }
 608             };
 609         } catch (IOException e) {
 610             throw new RuntimeException(e);
 611         }
 612     }
 613 
 614     /**
 615      * Used to generate the MatchStatement constructor invocation.
 616      */
 617     static class MatchRuleItem {
 618         private final String matchPattern;
 619         private final MethodInvokerItem invoker;
 620 
 621         MatchRuleItem(String matchPattern, MethodInvokerItem invoker) {
 622             this.matchPattern = matchPattern;
 623             this.invoker = invoker;
 624         }
 625 
 626         /**
 627          * @return a string which will construct the MatchStatement instance to match this pattern.
 628          */
 629         public String ruleBuilder() {
 630             return String.format("new MatchStatement(\"%s\", %s, %s.instance, %s)", invoker.methodName, matchPattern, invoker.wrapperClass(), invoker.argumentsListName());
 631         }
 632     }
 633 
 634     /**
 635      * Used to generate the wrapper class to invoke the code generation method.
 636      */
 637     static class MethodInvokerItem {
 638         final String methodName;
 639         final String nodeLIRBuilderClass;
 640         final ExecutableElement method;
 641         final List<? extends VariableElement> fields;
 642 
 643         MethodInvokerItem(String methodName, String nodeLIRBuilderClass, ExecutableElement method, List<? extends VariableElement> fields) {
 644             this.methodName = methodName;
 645             this.nodeLIRBuilderClass = nodeLIRBuilderClass;
 646             this.method = method;
 647             this.fields = fields;
 648         }
 649 
 650         String wrapperClass() {
 651             return "MatchGenerator_" + methodName;
 652         }
 653 
 654         String argumentsListName() {
 655             return methodName + "_arguments";
 656         }
 657     }
 658 
 659     static class MatchRuleDescriptor {
 660 
 661         final TypeElement topDeclaringType;
 662         final List<MatchRuleItem> matchRules = new ArrayList<>();
 663         private final Set<Element> originatingElements = new HashSet<>();
 664         public Set<String> positionDeclarations = new HashSet<>();
 665 
 666         /**
 667          * The mapping between elements with MatchRules and the wrapper class used invoke the code
 668          * generation after the match.
 669          */
 670         Map<String, MethodInvokerItem> invokers = new HashMap<>();
 671 
 672         /**
 673          * The set of packages which must be imported to refer the classes mentioned in matchRules.
 674          */
 675         Set<String> requiredPackages = new HashSet<>();
 676 
 677         MatchRuleDescriptor(TypeElement topDeclaringType) {
 678             this.topDeclaringType = topDeclaringType;
 679         }
 680     }
 681 
 682     private static TypeElement topDeclaringType(Element element) {
 683         Element enclosing = element.getEnclosingElement();
 684         if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) {
 685             return (TypeElement) element;
 686         }
 687         return topDeclaringType(enclosing);
 688     }
 689 
 690     /**
 691      * The element currently being processed.
 692      */
 693     private Element currentElement;
 694 
 695     /**
 696      * The current processing round.
 697      */
 698     private RoundEnvironment currentRound;
 699 
 700     @Override
 701     public boolean doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 702         if (roundEnv.processingOver()) {
 703             return true;
 704         }
 705 
 706         logMessage("Starting round %s\n", roundEnv);
 707 
 708         TypeElement matchRulesTypeElement = getTypeElement(MATCH_RULES_CLASS_NAME);
 709         TypeElement matchRuleTypeElement = getTypeElement(MATCH_RULE_CLASS_NAME);
 710 
 711         TypeMirror matchRulesTypeMirror = matchRulesTypeElement.asType();
 712         TypeMirror matchRuleTypeMirror = matchRuleTypeElement.asType();
 713 
 714         TypeElement matchableNodeTypeElement = getTypeElement(MATCHABLE_NODE_CLASS_NAME);
 715         TypeElement matchableNodesTypeElement = getTypeElement(MATCHABLE_NODES_CLASS_NAME);
 716 
 717         currentRound = roundEnv;
 718         try {
 719             for (Element element : roundEnv.getElementsAnnotatedWith(matchableNodeTypeElement)) {
 720                 currentElement = element;
 721                 logMessage("%s\n", element);
 722                 processMatchableNodes(element);
 723             }
 724             for (Element element : roundEnv.getElementsAnnotatedWith(matchableNodesTypeElement)) {
 725                 currentElement = element;
 726                 logMessage("%s\n", element);
 727                 processMatchableNodes(element);
 728             }
 729             // Define a TypeDescriptor for the generic node but don't enter it into the nodeTypes
 730             // table since it shouldn't be mentioned in match rules.
 731             TypeMirror valueTypeMirror = getTypeElement(VALUE_NODE_CLASS_NAME).asType();
 732             valueType = new TypeDescriptor(valueTypeMirror, "Value", "ValueNode", "org.graalvm.compiler.nodes", Collections.emptyList(), false, false, false);
 733 
 734             Map<TypeElement, MatchRuleDescriptor> map = new HashMap<>();
 735 
 736             for (Element element : roundEnv.getElementsAnnotatedWith(matchRuleTypeElement)) {
 737                 currentElement = element;
 738                 AnnotationMirror matchRule = getAnnotation(element, matchRuleTypeMirror);
 739                 List<AnnotationMirror> matchRuleAnnotations = Collections.singletonList(matchRule);
 740                 processMatchRules(map, element, matchRuleAnnotations);
 741             }
 742             for (Element element : roundEnv.getElementsAnnotatedWith(matchRulesTypeElement)) {
 743                 currentElement = element;
 744                 AnnotationMirror matchRules = getAnnotation(element, matchRulesTypeMirror);
 745                 List<AnnotationMirror> matchRuleAnnotations = getAnnotationValueList(matchRules, "value", AnnotationMirror.class);
 746                 processMatchRules(map, element, matchRuleAnnotations);
 747             }
 748 
 749             currentElement = null;
 750             for (MatchRuleDescriptor info : map.values()) {
 751                 createFiles(info);
 752             }
 753 
 754         } catch (Throwable t) {
 755             reportExceptionThrow(currentElement, t);
 756         } finally {
 757             currentElement = null;
 758             currentRound = null;
 759         }
 760 
 761         return true;
 762     }
 763 
 764     /**
 765      * Build up the type table to be used during parsing of the MatchRule.
 766      */
 767     private void processMatchableNodes(Element element) {
 768         if (!processedMatchableNodes.contains(element)) {
 769             try {
 770                 processedMatchableNodes.add(element);
 771 
 772                 List<AnnotationMirror> matchableNodeAnnotations;
 773                 AnnotationMirror mirror = getAnnotation(element, getType(MATCHABLE_NODES_CLASS_NAME));
 774                 if (mirror != null) {
 775                     matchableNodeAnnotations = getAnnotationValueList(mirror, "value", AnnotationMirror.class);
 776                 } else {
 777                     mirror = getAnnotation(element, getType(MATCHABLE_NODES_CLASS_NAME));
 778                     if (mirror != null) {
 779                         matchableNodeAnnotations = Collections.singletonList(mirror);
 780                     } else {
 781                         return;
 782                     }
 783                 }
 784 
 785                 TypeElement topDeclaringType = topDeclaringType(element);
 786                 for (AnnotationMirror matchableNode : matchableNodeAnnotations) {
 787                     processMatchableNode(element, topDeclaringType, matchableNode);
 788                 }
 789             } catch (Throwable t) {
 790                 reportExceptionThrow(element, t);
 791             }
 792         }
 793     }
 794 
 795     private void processMatchableNode(Element element, TypeElement topDeclaringType, AnnotationMirror matchable) {
 796         logMessage("processMatchableNode %s %s %s\n", topDeclaringType, element, matchable);
 797         String nodeClass;
 798         String nodePackage;
 799         TypeMirror nodeClassMirror = getAnnotationValue(matchable, "nodeClass", TypeMirror.class);
 800         if (nodeClassMirror == null) {
 801             throw new InternalError("Can't get mirror for node class " + element);
 802         }
 803         if (nodeClassMirror.toString().equals(MATCHABLE_NODE_CLASS_NAME)) {
 804             nodeClass = topDeclaringType.getQualifiedName().toString();
 805         } else {
 806             nodeClass = nodeClassMirror.toString();
 807         }
 808         TypeElement typeElement = processingEnv.getElementUtils().getTypeElement(nodeClass);
 809         if (typeElement == null) {
 810             printError(element, matchable, "Class \"%s\" cannot be resolved to a type", nodeClass);
 811             return;
 812         }
 813         nodePackage = findPackage(typeElement);
 814         assert nodeClass.startsWith(nodePackage);
 815         nodeClass = nodeClass.substring(nodePackage.length() + 1);
 816         assert nodeClass.endsWith("Node");
 817         String shortName = nodeClass.substring(0, nodeClass.length() - 4);
 818 
 819         Types typeUtils = processingEnv.getTypeUtils();
 820         TypeElement nodeClassElement = (TypeElement) typeUtils.asElement(nodeClassMirror);
 821         List<String> inputs = getAnnotationValueList(matchable, "inputs", String.class);
 822         for (String input : inputs) {
 823             boolean ok = false;
 824             TypeElement current = nodeClassElement;
 825             while (!ok && current != null) {
 826                 for (Element fieldElement : ElementFilter.fieldsIn(current.getEnclosedElements())) {
 827                     if (fieldElement.getSimpleName().toString().equals(input)) {
 828                         ok = true;
 829                         break;
 830                     }
 831                 }
 832                 TypeMirror theSuper = current.getSuperclass();
 833                 current = (TypeElement) typeUtils.asElement(theSuper);
 834             }
 835             if (!ok) {
 836                 printError(element, matchable, "Input named \"%s\" doesn't exist in %s", input, nodeClassElement.getSimpleName());
 837             }
 838         }
 839 
 840         boolean commutative = getAnnotationValue(matchable, "commutative", Boolean.class);
 841         boolean shareable = getAnnotationValue(matchable, "shareable", Boolean.class);
 842         boolean ignoresSideEffects = getAnnotationValue(matchable, "ignoresSideEffects", Boolean.class);
 843         declareType(nodeClassMirror, shortName, nodeClass, nodePackage, inputs, commutative, shareable, ignoresSideEffects, element);
 844     }
 845 
 846     private void processMatchRules(Map<TypeElement, MatchRuleDescriptor> map, Element element, List<AnnotationMirror> matchRules) {
 847         if (!processedMatchRules.contains(element)) {
 848             try {
 849                 processedMatchRules.add(element);
 850 
 851                 // The annotation element type should ensure this is true.
 852                 assert element instanceof ExecutableElement;
 853 
 854                 findMatchableNodes(element);
 855 
 856                 TypeElement topDeclaringType = topDeclaringType(element);
 857                 MatchRuleDescriptor info = map.get(topDeclaringType);
 858                 if (info == null) {
 859                     info = new MatchRuleDescriptor(topDeclaringType);
 860                     map.put(topDeclaringType, info);
 861                 }
 862                 for (AnnotationMirror matchRule : matchRules) {
 863                     processMatchRule((ExecutableElement) element, info, matchRule);
 864                 }
 865             } catch (Throwable t) {
 866                 reportExceptionThrow(element, t);
 867             }
 868         }
 869     }
 870 
 871     /**
 872      * Search the super types of element for MatchableNode definitions. Any superclass or super
 873      * interface can contain definitions of matchable nodes.
 874      *
 875      * @param element
 876      */
 877     private void findMatchableNodes(Element element) {
 878         processMatchableNodes(element);
 879         Element enclosing = element.getEnclosingElement();
 880         while (enclosing != null) {
 881             if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
 882                 TypeElement current = (TypeElement) enclosing;
 883                 while (current != null) {
 884                     processMatchableNodes(current);
 885                     for (TypeMirror intf : current.getInterfaces()) {
 886                         Element interfaceElement = typeUtils().asElement(intf);
 887                         processMatchableNodes(interfaceElement);
 888                         // Recurse
 889                         findMatchableNodes(interfaceElement);
 890                     }
 891                     TypeMirror theSuper = current.getSuperclass();
 892                     current = (TypeElement) typeUtils().asElement(theSuper);
 893                 }
 894             }
 895             enclosing = enclosing.getEnclosingElement();
 896         }
 897     }
 898 
 899     private Types typeUtils() {
 900         return processingEnv.getTypeUtils();
 901     }
 902 
 903     private void processMatchRule(ExecutableElement method, MatchRuleDescriptor info, AnnotationMirror matchRule) {
 904         logMessage("processMatchRule %s\n", method);
 905 
 906         Types typeUtils = typeUtils();
 907 
 908         if (!method.getModifiers().contains(Modifier.PUBLIC)) {
 909             printError(method, "MatchRule method %s must be public", method.getSimpleName());
 910             return;
 911         }
 912         if (method.getModifiers().contains(Modifier.STATIC)) {
 913             printError(method, "MatchRule method %s must be non-static", method.getSimpleName());
 914             return;
 915         }
 916 
 917         try {
 918             TypeMirror returnType = method.getReturnType();
 919             if (!typeUtils.isSameType(returnType, processingEnv.getElementUtils().getTypeElement(COMPLEX_MATCH_RESULT_CLASS_NAME).asType())) {
 920                 printError(method, "MatchRule method return type must be %s", COMPLEX_MATCH_RESULT_CLASS_NAME);
 921                 return;
 922             }
 923 
 924             String rule = getAnnotationValue(matchRule, "value", String.class);
 925             RuleParser parser = new RuleParser(rule);
 926             ArrayList<TypeDescriptor> expectedTypes = parser.capturedTypes();
 927             ArrayList<String> expectedNames = parser.capturedNames();
 928             List<? extends VariableElement> actualParameters = method.getParameters();
 929             if (expectedTypes.size() + 1 < actualParameters.size()) {
 930                 printError(method, "Too many arguments for match method %s != %s", expectedTypes.size() + 1, actualParameters.size());
 931                 return;
 932             }
 933 
 934             // Walk through the parameters to the method and see if they exist in the match rule.
 935             // The order doesn't matter but only names mentioned in the rule can be used and they
 936             // must be assignment compatible.
 937             for (VariableElement parameter : actualParameters) {
 938                 String name = parameter.getSimpleName().toString();
 939                 int nameIndex = expectedNames.indexOf(name);
 940                 if (nameIndex == -1) {
 941                     printError(method, "Argument \"%s\" isn't captured in the match rule", name);
 942                     return;
 943                 }
 944                 TypeMirror type = parameter.asType();
 945                 if (!typeUtils.isAssignable(expectedTypes.get(nameIndex).mirror, type)) {
 946                     printError(method, "Captured value \"%s\" of type %s is not assignable to argument of type %s", name, expectedTypes.get(nameIndex).mirror, type);
 947                     return;
 948                 }
 949             }
 950 
 951             String methodName = method.getSimpleName().toString();
 952             MethodInvokerItem invoker = info.invokers.get(methodName);
 953             if (invoker == null) {
 954                 invoker = new MethodInvokerItem(methodName, topDeclaringType(method).getSimpleName().toString(), method, actualParameters);
 955                 info.invokers.put(methodName, invoker);
 956             } else if (invoker.method != method) {
 957                 // This could be supported but it's easier if they are unique since the names
 958                 // are used in log output and snippet counters.
 959                 printError(method, "Use unique method names for match methods: %s.%s != %s.%s", method.getReceiverType(), method.getSimpleName(), invoker.method.getReceiverType(),
 960                                 invoker.method.getSimpleName());
 961                 return;
 962             }
 963 
 964             Element enclosing = method.getEnclosingElement();
 965             String declaringClass = "";
 966             String separator = "";
 967             Set<Element> originatingElementsList = info.originatingElements;
 968             originatingElementsList.add(method);
 969             while (enclosing != null) {
 970                 if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE || enclosing.getKind() == ElementKind.ENUM) {
 971                     if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
 972                         printError(method, "MatchRule cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
 973                         return;
 974                     }
 975                     originatingElementsList.add(enclosing);
 976                     declaringClass = enclosing.getSimpleName() + separator + declaringClass;
 977                     separator = ".";
 978                 } else if (enclosing.getKind() == ElementKind.PACKAGE) {
 979                     break;
 980                 } else {
 981                     printError(method, "MatchRule cannot be declared in a %s", enclosing.getKind().name().toLowerCase());
 982                     return;
 983                 }
 984                 enclosing = enclosing.getEnclosingElement();
 985             }
 986 
 987             originatingElementsList.addAll(parser.originatingElements);
 988             info.requiredPackages.addAll(parser.requiredPackages);
 989 
 990             // Accumulate any position declarations.
 991             parser.generatePositionDeclarations(info.positionDeclarations);
 992 
 993             List<String> matches = parser.generateVariants();
 994             for (String match : matches) {
 995                 info.matchRules.add(new MatchRuleItem(match, invoker));
 996             }
 997         } catch (RuleParseError e) {
 998             printError(method, matchRule, e.getMessage());
 999         }
1000     }
1001 
1002     private Element elementForMessage(Element e) {
1003         if (currentRound != null && !currentRound.getRootElements().contains(e) && currentElement != null) {
1004             return currentElement;
1005         }
1006         return e;
1007     }
1008 
1009     private void printError(Element annotatedElement, String format, Object... args) {
1010         Element e = elementForMessage(annotatedElement);
1011         String prefix = e == annotatedElement ? "" : annotatedElement + ": ";
1012         processingEnv.getMessager().printMessage(Kind.ERROR, prefix + String.format(format, args), e);
1013     }
1014 
1015     private void printError(Element annotatedElement, AnnotationMirror annotation, String format, Object... args) {
1016         Element e = elementForMessage(annotatedElement);
1017         String prefix = e == annotatedElement ? "" : annotation + " on " + annotatedElement + ": ";
1018         processingEnv.getMessager().printMessage(Kind.ERROR, prefix + String.format(format, args), e, annotation);
1019     }
1020 }