1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24package compiler.compilercontrol.share.method;
25
26import compiler.compilercontrol.share.method.MethodDescriptor.PatternType;
27import compiler.compilercontrol.share.method.MethodDescriptor.Separator;
28import compiler.compilercontrol.share.pool.PoolHelper;
29import jdk.test.lib.util.Pair;
30import jdk.test.lib.util.Triple;
31import jdk.test.lib.Utils;
32
33import java.lang.reflect.Executable;
34import java.util.ArrayList;
35import java.util.EnumSet;
36import java.util.List;
37import java.util.concurrent.Callable;
38import java.util.function.Function;
39
40/**
41 * Generates combinations of method descriptors from the pool of methods
42 */
43public class MethodGenerator {
44    private static final List<Pair<Executable, Callable<?>>> METHODS =
45            new PoolHelper().getAllMethods(PoolHelper.METHOD_FILTER);
46    // Different combinations of patterns
47    private static final List<Combination<PatternType>> PATTERNS_LIST;
48    // Different combinations of separators
49    private static final List<Combination<Separator>> SEPARATORS_LIST;
50    // List of functions that modify elements
51    private static final List<Function<String, String>> ELEMENT_MUTATORS;
52
53    static {
54        PATTERNS_LIST =
55                generate(EnumSet.allOf(PatternType.class),
56                        EnumSet.allOf(PatternType.class),
57                        EnumSet.of(PatternType.ANY, PatternType.EXACT));
58        SEPARATORS_LIST =
59                generate(EnumSet.of(Separator.SLASH, Separator.DOT),
60                        EnumSet.complementOf(EnumSet.of(Separator.NONE)),
61                        EnumSet.of(Separator.COMMA, Separator.SPACE,
62                                Separator.NONE));
63        ELEMENT_MUTATORS = generateMutators();
64    }
65
66    // Test method
67    public static void main(String[] args) {
68        MethodGenerator methodGenerator = new MethodGenerator();
69        List<MethodDescriptor> tests = methodGenerator.getTests();
70        tests.forEach(System.out::println);
71    }
72
73    /**
74     * Generates random method descriptor
75     *
76     * @param executable executable used to generate descriptor
77     * @return MethodDescriptor instance
78     */
79    public MethodDescriptor generateRandomDescriptor(Executable executable) {
80        Combination<PatternType> patterns =
81                Utils.getRandomElement(PATTERNS_LIST);
82        Combination<Separator> separators =
83                Utils.getRandomElement(SEPARATORS_LIST);
84        // Create simple mutators for signature generation
85        List<Function<String, String>> signMutators = new ArrayList<>();
86        signMutators.add(input -> input);
87        signMutators.add(input -> "");
88        Combination<Function<String, String>> mutators = new Combination<>(
89                Utils.getRandomElement(ELEMENT_MUTATORS),
90                Utils.getRandomElement(ELEMENT_MUTATORS),
91                // use only this type of mutators
92                Utils.getRandomElement(signMutators));
93        return makeMethodDescriptor(executable, patterns,
94                separators, mutators);
95    }
96
97    /**
98     * Compile command signature that looks like java/lang/String.indexOf
99     * http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BABDDFII
100     *
101     * @param executable executable used to generate descriptor
102     * @return MethodDescriptor instance
103     */
104    public static MethodDescriptor commandDescriptor(Executable executable) {
105        MethodDescriptor md = new MethodDescriptor(executable);
106        md.aClass.setSeparator(Separator.SLASH);
107        md.aMethod.setSeparator(Separator.DOT);
108        md.aSignature.setSeparator(Separator.NONE);
109        return md;
110    }
111
112    /**
113     * Compile command signature that looks like java.lang.String::indexOf
114     *
115     * @param executable executable used to generate descriptor
116     * @return MethodDescriptor instance
117     */
118    public static MethodDescriptor logDescriptor(Executable executable) {
119        MethodDescriptor md = new MethodDescriptor(executable);
120        md.aClass.setSeparator(Separator.DOT);
121        md.aMethod.setSeparator(Separator.DOUBLECOLON);
122        md.aSignature.setSeparator(Separator.NONE);
123        return md;
124    }
125
126    /**
127     * Method descriptor that matches any method. Its full signature is *.*
128     *
129     * @param executable executable used to generate descriptor
130     * @return MethodDescriptor instance
131     */
132    public static MethodDescriptor anyMatchDescriptor(Executable executable) {
133        MethodDescriptor md = new MethodDescriptor(executable);
134        Combination<PatternType> patterns = new Combination<>(PatternType.ANY,
135                PatternType.ANY, PatternType.ANY);
136        md.aClass.setSeparator(Separator.SLASH);
137        md.aMethod.setSeparator(Separator.DOT);
138        md.aSignature.setSeparator(Separator.NONE);
139        md.setPatterns(patterns);
140        return md;
141    }
142
143    /**
144     * Generates a list of method patterns from the pool of methods
145     *
146     * @return a list of test cases
147     */
148    public List<MethodDescriptor> getTests() {
149        List<MethodDescriptor> list = new ArrayList<>();
150        METHODS.forEach(pair -> list.addAll(getTests(pair.first)));
151        return list;
152    }
153
154    /**
155     * Generates all combinations of method descriptors for a given executable
156     *
157     * @param executable executable for which the different combination is built
158     * @return list of method descriptors
159     */
160    public List<MethodDescriptor> getTests(Executable executable) {
161        List<MethodDescriptor> list = new ArrayList<>();
162        for (Combination<PatternType> patterns : PATTERNS_LIST) {
163            for (Combination<Separator> separators : SEPARATORS_LIST) {
164                for (Function<String, String> classGen : ELEMENT_MUTATORS) {
165                    for (Function<String, String> methodGen :
166                            ELEMENT_MUTATORS) {
167                        for (Function<String, String> signatureGen :
168                                ELEMENT_MUTATORS) {
169                            list.add(makeMethodDescriptor(executable,
170                                    patterns, separators,
171                                    new Combination<>(classGen, methodGen,
172                                        signatureGen)));
173                        }
174                    }
175                }
176            }
177        }
178        return list;
179    }
180
181    /**
182     * Creates method descriptor from the given executable,
183     * patterns and separators for its elements
184     */
185    private MethodDescriptor makeMethodDescriptor(
186            Executable executable,
187            Combination<PatternType> patterns,
188            Combination<Separator> separators,
189            Combination<Function<String, String>> mutators) {
190        MethodDescriptor methodDescriptor = new MethodDescriptor(executable);
191        methodDescriptor.setSeparators(separators);
192        methodDescriptor.applyMutates(mutators);
193        methodDescriptor.setPatterns(patterns);
194        return methodDescriptor;
195    }
196
197    /**
198     * Creates a list of functions that change given string
199     */
200    private static List<Function<String, String>> generateMutators() {
201        List<Function<String, String>> elements = new ArrayList<>();
202        // Use the input itself
203        elements.add(input -> input);
204        // Use half of the input string
205        elements.add(input -> input.substring(input.length() / 2));
206        // Add nonexistent element
207        elements.add(input -> "nonexistent");
208        // Use left and right angle brackets
209        elements.add(input -> "<" + input + ">");
210        // Embed * inside
211        elements.add(input -> embed(input, "*"));
212        // ** as a whole element
213        elements.add(input -> "**");
214        // Embed JLS-invalid letters
215        elements.add(input -> embed(input, "@%"));
216        elements.add(input -> embed(input, "]"));
217        // Use JLS-invalid letters
218        elements.add(input -> "-");
219        elements.add(input -> "+");
220        elements.add(input -> ")" + input);
221        elements.add(input -> "{" + input + "}");
222        // Add valid Java identifier start char
223        elements.add(input -> "_" + input);
224        elements.add(input -> "$" + input);
225        elements.add(input -> "0" + input);
226
227        /* TODO: uncomment this together with the fix for 8140631
228        // Unicode characters
229        elements.add(input -> embed(input, "\u0001"));
230        elements.add(input -> embed(input, "\u007F"));
231        // Combining character
232        elements.add(input -> embed(input, "\u0300"));
233        elements.add(input -> embed(input, "\u0306"));
234        // Supplementary character
235        elements.add(input -> new String(Character.toChars(0x1F64C)));
236        */
237        return elements;
238    }
239
240    /**
241     * Embeds one string inside another one
242     *
243     * @param target  target source string
244     * @param element string to be embedded into target string
245     * @return result string
246     */
247    private static String embed(String target, String element) {
248        int mid = target.length() / 2;
249        String begin = target.substring(0, mid);
250        String end = target.substring(mid);
251        return begin + element + end;
252    }
253
254    /**
255     * Generates triples from the given enum sets
256     * for each of the method elements
257     *
258     * @param classSet  set of allowed elements for class
259     * @param methodSet set of allowed elements for method
260     * @param signSet   set of allowed elements for signature
261     * @param <E>       type of generated triples
262     * @return list of triples
263     */
264    private static <E extends Enum<E>> List<Combination<E>> generate(
265            EnumSet<E> classSet, EnumSet<E> methodSet, EnumSet<E> signSet) {
266        List<Combination<E>> list = new ArrayList<>();
267        classSet.forEach(clsElement ->
268            methodSet.forEach(methodElement ->
269                signSet.forEach(signElement ->
270                    list.add(new Combination<>(clsElement, methodElement,
271                            signElement))
272                )
273            )
274        );
275        return list;
276    }
277
278    private static class Combination<T> extends Triple<T, T, T> {
279        public Combination(T first, T second, T third) {
280            super(first, second, third);
281        }
282    }
283}
284