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