1/*
2 * Copyright (c) 2014, 2015, 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;
24
25import static org.graalvm.compiler.debug.GraalDebugConfig.Options.LogVerbose;
26
27import java.util.ArrayList;
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31import java.util.Map.Entry;
32
33import org.graalvm.compiler.core.gen.NodeMatchRules;
34import org.graalvm.compiler.debug.Debug;
35import org.graalvm.compiler.debug.Debug.Scope;
36import org.graalvm.compiler.debug.GraalError;
37import org.graalvm.compiler.graph.Edges;
38import org.graalvm.compiler.graph.Node;
39import org.graalvm.compiler.graph.NodeClass;
40import org.graalvm.compiler.graph.Position;
41import org.graalvm.compiler.serviceprovider.GraalServices;
42
43public class MatchRuleRegistry {
44
45    /**
46     * Convert a list of field names into {@link org.graalvm.compiler.graph.Position} objects that
47     * can be used to read them during a match. The names should already have been confirmed to
48     * exist in the type.
49     *
50     * @param nodeClass
51     * @param names
52     * @return an array of Position objects corresponding to the named fields.
53     */
54    public static Position[] findPositions(NodeClass<? extends Node> nodeClass, String[] names) {
55        Position[] result = new Position[names.length];
56        for (int i = 0; i < names.length; i++) {
57            Edges edges = nodeClass.getInputEdges();
58            for (int e = 0; e < edges.getDirectCount(); e++) {
59                if (names[i].equals(edges.getName(e))) {
60                    result[i] = new Position(edges, e, Node.NOT_ITERABLE);
61                }
62            }
63            if (result[i] == null) {
64                throw new GraalError("unknown field \"%s\" in class %s", names[i], nodeClass);
65            }
66        }
67        return result;
68    }
69
70    private static final HashMap<Class<? extends NodeMatchRules>, Map<Class<? extends Node>, List<MatchStatement>>> registry = new HashMap<>();
71
72    /**
73     * Collect all the {@link MatchStatement}s defined by the superclass chain of theClass.
74     *
75     * @param theClass
76     * @return the set of {@link MatchStatement}s applicable to theClass.
77     */
78    @SuppressWarnings("try")
79    public static synchronized Map<Class<? extends Node>, List<MatchStatement>> lookup(Class<? extends NodeMatchRules> theClass) {
80        Map<Class<? extends Node>, List<MatchStatement>> result = registry.get(theClass);
81
82        if (result == null) {
83            Map<Class<? extends Node>, List<MatchStatement>> rules = createRules(theClass);
84            registry.put(theClass, rules);
85            assert registry.get(theClass) == rules;
86            result = rules;
87
88            if (LogVerbose.getValue()) {
89                try (Scope s = Debug.scope("MatchComplexExpressions")) {
90                    Debug.log("Match rules for %s", theClass.getSimpleName());
91                    for (Entry<Class<? extends Node>, List<MatchStatement>> entry : result.entrySet()) {
92                        Debug.log("  For node class: %s", entry.getKey());
93                        for (MatchStatement statement : entry.getValue()) {
94                            Debug.log("    %s", statement.getPattern());
95                        }
96                    }
97                }
98            }
99        }
100
101        if (result.size() == 0) {
102            return null;
103        }
104        return result;
105    }
106
107    /*
108     * This is a separate, public method so that external clients can create rules with a custom
109     * lookup and without the default caching behavior.
110     */
111    public static Map<Class<? extends Node>, List<MatchStatement>> createRules(Class<? extends NodeMatchRules> theClass) {
112        HashMap<Class<? extends NodeMatchRules>, MatchStatementSet> matchSets = new HashMap<>();
113        Iterable<MatchStatementSet> sl = GraalServices.load(MatchStatementSet.class);
114        for (MatchStatementSet rules : sl) {
115            matchSets.put(rules.forClass(), rules);
116        }
117
118        // Walk the class hierarchy collecting lists and merge them together. The subclass
119        // rules are first which gives them preference over earlier rules.
120        Map<Class<? extends Node>, List<MatchStatement>> rules = new HashMap<>();
121        Class<?> currentClass = theClass;
122        do {
123            MatchStatementSet matchSet = matchSets.get(currentClass);
124            if (matchSet != null) {
125                List<MatchStatement> statements = matchSet.statements();
126                for (MatchStatement statement : statements) {
127                    Class<? extends Node> nodeClass = statement.getPattern().nodeClass();
128                    List<MatchStatement> current = rules.get(nodeClass);
129                    if (current == null) {
130                        current = new ArrayList<>();
131                        rules.put(nodeClass, current);
132                    }
133                    current.add(statement);
134                }
135            }
136            currentClass = currentClass.getSuperclass();
137        } while (currentClass != NodeMatchRules.class);
138        return rules;
139    }
140}
141