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