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