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 */
23package org.graalvm.compiler.core.match.processor;
24
25import java.io.FileWriter;
26import java.io.IOException;
27import java.io.PrintWriter;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.HashSet;
31import java.util.List;
32import java.util.Set;
33import java.util.regex.Matcher;
34import java.util.regex.Pattern;
35
36import javax.annotation.processing.AbstractProcessor;
37import javax.annotation.processing.Filer;
38import javax.annotation.processing.RoundEnvironment;
39import javax.annotation.processing.SupportedAnnotationTypes;
40import javax.lang.model.SourceVersion;
41import javax.lang.model.element.AnnotationMirror;
42import javax.lang.model.element.AnnotationValue;
43import javax.lang.model.element.Element;
44import javax.lang.model.element.ElementKind;
45import javax.lang.model.element.ExecutableElement;
46import javax.lang.model.element.Modifier;
47import javax.lang.model.element.Name;
48import javax.lang.model.element.PackageElement;
49import javax.lang.model.element.TypeElement;
50import javax.lang.model.element.VariableElement;
51import javax.lang.model.type.MirroredTypeException;
52import javax.lang.model.type.TypeMirror;
53import javax.lang.model.util.AbstractAnnotationValueVisitor7;
54import javax.lang.model.util.ElementFilter;
55import javax.lang.model.util.Types;
56import javax.tools.Diagnostic.Kind;
57import javax.tools.FileObject;
58import javax.tools.JavaFileObject;
59import javax.tools.StandardLocation;
60
61import org.graalvm.compiler.core.gen.NodeMatchRules;
62import org.graalvm.compiler.core.match.ComplexMatchResult;
63import org.graalvm.compiler.core.match.MatchRule;
64import org.graalvm.compiler.core.match.MatchRules;
65import org.graalvm.compiler.core.match.MatchStatement;
66import org.graalvm.compiler.core.match.MatchStatementSet;
67import org.graalvm.compiler.core.match.MatchableNode;
68import org.graalvm.compiler.core.match.MatchableNodes;
69import org.graalvm.compiler.debug.GraalError;
70import org.graalvm.compiler.graph.Position;
71import org.graalvm.compiler.nodes.ValueNode;
72import org.graalvm.compiler.serviceprovider.ServiceProvider;
73import org.graalvm.util.Equivalence;
74import org.graalvm.util.EconomicMap;
75import 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"})
91public 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}
1132