1/*
2 * Copyright (c) 2011, 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 */
23
24/*
25 * @test
26 * @bug 7017664 7036906
27 * @summary Basher for CompoundScopes
28 * @modules jdk.compiler/com.sun.tools.javac.code
29 *          jdk.compiler/com.sun.tools.javac.file
30 *          jdk.compiler/com.sun.tools.javac.util
31 */
32
33import java.util.Random;
34import java.util.Map;
35import java.util.HashMap;
36import com.sun.tools.javac.util.*;
37import com.sun.tools.javac.code.*;
38import com.sun.tools.javac.code.Scope.*;
39import com.sun.tools.javac.code.Symbol.*;
40import com.sun.tools.javac.file.JavacFileManager;
41
42public class CompoundScopeTest {
43    public static void main(String... args) throws Exception {
44        new CompoundScopeTest().run(args);
45    }
46
47    static final int MAX_SYMBOLS_COUNT = 20;
48    static final int PASSES = 10;
49
50    void run(String... args) throws Exception {
51        int count = PASSES;
52
53        for (int i = 0; i < args.length; i++) {
54            String arg = args[i];
55            if (arg.equals("-seed") && (i + 1 < args.length))
56                seed = Long.parseLong(args[++i]);
57            else if(arg.equals("-tests") && (i + 1 < args.length))
58                count = Integer.parseInt(args[++i]);
59            else
60                throw new Exception("unknown arg: " + arg);
61        }
62
63        rgen = new Random(seed);
64
65        for (int i = 0; i < count; i++) {
66            Test t = new Test();
67            t.run();
68        }
69
70        if (errors > 0)
71            throw new Exception(errors + " errors found");
72    }
73
74    /**
75     * Write a message to stderr.
76     */
77    void log(String msg) {
78        System.err.println(msg);
79    }
80
81    /**
82     * Write an error message to stderr.
83     */
84    void error(String msg) {
85        System.err.println("Error: " + msg);
86        errors++;
87    }
88
89    Random rgen;
90    long seed = 0;
91
92    int errors;
93
94    /** Class to encapsulate a test run. */
95    class Test {
96
97        List<Symbol> elems = List.nil();
98        Map<Name, List<Symbol>> shadowedMap = new HashMap<Name, List<Symbol>>();
99
100        /** Run the test. */
101        void run() throws Exception {
102            log ("starting test");
103            setup();
104            Scope[] scopes = { createScope(rgen.nextInt(MAX_SYMBOLS_COUNT)),
105                               createScope(rgen.nextInt(MAX_SYMBOLS_COUNT)),
106                               createScope(rgen.nextInt(MAX_SYMBOLS_COUNT)) };
107            boolean[][] scopeNesting = { {false, true, false, true},
108                                   {false, true, true, true},
109                                   {false, false, true, true} };
110            /**
111             * We want to generate (and check) the following compound scopes:
112             * C1 = C(S1, S2, S3)
113             * C2 = C((S1, S2), S3)
114             * C3 = C(S1, (S2, S3))
115             * C3 = C(C(S1, S2, S3))
116             */
117            for (int i = 0 ; i < 4 ; i ++) {
118                CompoundScope root = new CompoundScope(symtab.noSymbol);
119                CompoundScope sub = new CompoundScope(symtab.noSymbol);
120                boolean subAdded = false;
121                for (int sc = 0 ; sc < 3 ; sc ++) {
122                    if (scopeNesting[sc][i]) {
123                        sub.prependSubScope(scopes[sc]);
124                        if (!subAdded) {
125                            root.prependSubScope(sub);
126                            subAdded = true;
127                        }
128                    } else {
129                        root.prependSubScope(scopes[sc]);
130                    }
131                }
132                log("testing scope: " + root);
133                checkElems(root, null);
134                checkElems(root, new OddFilter());
135                checkShadowed(root, null);
136                checkShadowed(root, new OddFilter());
137            }
138        }
139
140        class OddFilter implements Filter<Symbol> {
141            public boolean accepts(Symbol s) {
142                Name numPart = s.name.subName(1, s.name.length());
143                return Integer.parseInt(numPart.toString()) % 2 != 0;
144            }
145        }
146
147        /**
148         * Create a scope containing a given number of synthetic symbols
149         */
150        Scope createScope(int nelems) {
151            WriteableScope s = WriteableScope.create(symtab.noSymbol);
152            for (int i = 0 ; i < nelems ; i++) {
153                Symbol sym = new TypeVariableSymbol(0, names.fromString("s" + i), null, null);
154                s.enter(sym);
155                elems = elems.prepend(sym);
156                List<Symbol> shadowed = shadowedMap.get(sym.name);
157                if (shadowed == null) {
158                    shadowed = List.nil();
159                }
160                shadowedMap.put(sym.name, shadowed.prepend(sym));
161            }
162            return s;
163        }
164
165        /**
166         * Setup compiler context
167         */
168        void setup() {
169            log ("setup");
170            context = new Context();
171            JavacFileManager.preRegister(context); // required by ClassReader which is required by Symtab
172            names = Names.instance(context);       // Name.Table impls tied to an instance of Names
173            symtab = Symtab.instance(context);
174        }
175
176        /**
177         * Check that CompoundScope.getElements() correctly visits all symbols
178         * in all subscopes (in the correct order)
179         */
180        void checkElems(CompoundScope cs, Filter<Symbol> sf) {
181            int count = 0;
182            ListBuffer<Symbol> found = new ListBuffer<>();
183            List<Symbol> allSymbols = sf == null ?
184                    elems :
185                    filter(elems, sf);
186            int expectedCount = allSymbols.length();
187            for (Symbol s : sf == null ? cs.getSymbols() : cs.getSymbols(sf)) {
188                checkSameSymbols(s, allSymbols.head);
189                allSymbols = allSymbols.tail;
190                found.append(s);
191                count++;
192            }
193            if (count != expectedCount) {
194                error("CompoundScope.getElements() did not returned enough symbols");
195            }
196        }
197
198        /**
199         * Check that CompoundScope.getElements() correctly visits all symbols
200         * with a given name in all subscopes (in the correct order)
201         */
202        void checkShadowed(CompoundScope cs, Filter<Symbol> sf) {
203            for (Map.Entry<Name, List<Symbol>> shadowedEntry : shadowedMap.entrySet()) {
204                int count = 0;
205                List<Symbol> shadowed = sf == null ?
206                    shadowedEntry.getValue() :
207                    filter(shadowedEntry.getValue(), sf);
208                int expectedCount = shadowed.length();
209                Name name = shadowedEntry.getKey();
210                for (Symbol s : sf == null ? cs.getSymbolsByName(name) : cs.getSymbolsByName(name, sf)) {
211                    checkSameSymbols(s, shadowed.head);
212                    shadowed = shadowed.tail;
213                    count++;
214                }
215                if (count != expectedCount) {
216                    error("CompoundScope.lookup() did not returned enough symbols for name " + name);
217                }
218            }
219        }
220
221        List<Symbol> filter(List<Symbol> elems, Filter<Symbol> sf) {
222            ListBuffer<Symbol> res = new ListBuffer<>();
223            for (Symbol s : elems) {
224                if (sf.accepts(s)) {
225                    res.append(s);
226                }
227            }
228            return res.toList();
229        }
230
231        void checkSameSymbols(Symbol found, Symbol req) {
232            if (found != req) {
233                error("Symbol mismatch - found    : " + found + ":" + found.hashCode() + "\n" +
234                      "                  required : " + req + ":" + req.hashCode());
235            }
236        }
237
238        Context context;
239        Symtab symtab;
240        Names names;
241    }
242}
243