DupUnsharedTest.java revision 3294:9adfb22ff08f
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 */
23
24/*
25 * @test
26 * @summary WriteableScope.dupUnshared not working properly for shared Scopes.
27 * @modules jdk.compiler/com.sun.tools.javac.code
28 *          jdk.compiler/com.sun.tools.javac.file
29 *          jdk.compiler/com.sun.tools.javac.util
30 */
31
32import com.sun.tools.javac.util.*;
33import com.sun.tools.javac.code.*;
34import com.sun.tools.javac.code.Scope.*;
35import com.sun.tools.javac.code.Symbol.*;
36import com.sun.tools.javac.file.JavacFileManager;
37import java.lang.reflect.Field;
38import java.lang.reflect.Method;
39import java.util.Collections;
40import java.util.IdentityHashMap;
41import java.util.Objects;
42import java.util.Set;
43
44public class DupUnsharedTest {
45    public static void main(String... args) throws Exception {
46        new DupUnsharedTest().run();
47    }
48
49    Context context;
50    Names names;
51    Symtab symtab;
52    Name a;
53    Name b;
54    int errors;
55
56    public DupUnsharedTest() {
57        context = new Context();
58        JavacFileManager.preRegister(context); // required by ClassReader which is required by Symtab
59        names = Names.instance(context);
60        symtab = Symtab.instance(context);
61        a = names.fromString("a");
62        b = names.fromString("b");
63    }
64
65    void run() throws Exception {
66        runScopeContentTest();
67        runClashTest();
68
69        if (errors > 0)
70            throw new AssertionError("Errors detected (" + errors + ").");
71    }
72
73    void runScopeContentTest() throws Exception {
74        Set<Symbol> expected = Collections.newSetFromMap(new IdentityHashMap<>() {});
75        Set<Symbol> notExpected = Collections.newSetFromMap(new IdentityHashMap<>());
76        WriteableScope s1 = WriteableScope.create(symtab.unnamedModule.unnamedPackage);
77        ClassSymbol acceptSym = symtab.arrayClass;
78        s1.enter(acceptSym);
79        expected.add(acceptSym);
80        WriteableScope s2 = s1.dup();
81        fillScope(s2, notExpected, a);
82        WriteableScope s3 = s2.dup();
83        fillScope(s3, notExpected, b);
84        WriteableScope s4 = s1.dupUnshared();
85        assertEquals(toSet(s4.getSymbols()), expected);
86        assertEquals(toSet(s4.getSymbolsByName(a)), Collections.emptySet());
87        assertEquals(toSet(s4.getSymbolsByName(b)), Collections.emptySet());
88        assertEquals(toSet(s4.getSymbolsByName(acceptSym.name)), expected);
89        for (Symbol sym : notExpected) {
90            try {
91                s4.remove(sym);
92            } catch (Exception ex) {
93                System.err.println("s4.remove(" + sym + "); crashes with exception:");
94                ex.printStackTrace();
95                errors++;
96            }
97        }
98    }
99
100    void fillScope(WriteableScope scope, Set<Symbol> notExpected, Name name) {
101        VarSymbol var1 = new VarSymbol(0, name, Type.noType, symtab.arrayClass);
102        VarSymbol var2 = new VarSymbol(0, name, Type.noType, symtab.autoCloseableClose.owner);
103        scope.enter(var1);
104        scope.enter(var2);
105        scope.remove(var1);
106        notExpected.add(var1);
107        notExpected.add(var2);
108    }
109
110    Set<Symbol> toSet(Iterable<Symbol> it) {
111        Set<Symbol> result = Collections.newSetFromMap(new IdentityHashMap<>() {});
112
113        for (Symbol sym : it) {
114            result.add(sym);
115        }
116
117        return result;
118    }
119
120    void assertEquals(Set<Symbol> set1, Set<Symbol> set2) {
121        if (!Objects.equals(set1, set2)) {
122            System.err.println("Sets are not equals: s1=" + set1 + "; s2=" + set2);
123            errors++;
124        }
125    }
126
127    /**
128     * This tests tests the following situation.
129     * - consider empty Scope S1
130     * - a Symbol with name 'A' is added into S1
131     * - S1 is dupped into S2
132     * - a Symbol with name 'B', clashing with 'A', is added into S2
133     * - so the table now looks like: [..., A, ..., B, ...]
134     * - S2 is doubled. As a consequence, the table is re-hashed, and looks like:
135     *   [..., B, ..., A, ...] (note that re-hashing goes from the end, hence the original order).
136     * - B has been chosen so that it clashes in the doubled scope as well. So when looking up 'A',
137     *   B is found (and rejected) first, and only then the A's bucket is tested.
138     * - S2 is dupUshared - the resulting table needs to look like: [..., /sentinel/, ..., A, ...], not
139     *   [..., null, ..., A, ...], as in the latter case lookups would see 'null' while looking for
140     *   'A' and would stop the search prematurely.
141     */
142    void runClashTest() throws Exception {
143        WriteableScope emptyScope = WriteableScope.create(symtab.unnamedModule.unnamedPackage);
144        Field tableField = emptyScope.getClass().getDeclaredField("table");
145        tableField.setAccessible(true);
146        Method dble = emptyScope.getClass().getDeclaredMethod("dble");
147        dble.setAccessible(true);
148        Method getIndex = emptyScope.getClass().getDeclaredMethod("getIndex", Name.class);
149        getIndex.setAccessible(true);
150
151        int tries = 0;
152
153        //find a name that will be in the first bucket in table (so that a conflicting name
154        //will be in placed in a bucket after this one).
155        Name first = names.fromString("a");
156        while ((Integer) getIndex.invoke(emptyScope, first) != 0) {
157            if (tries++ > MAX_TRIES) {
158                System.err.println("could not find a name that would be placed in the first bucket");
159                errors++;
160                return ;
161            }
162            first = names.fromString("a" + first.toString());
163        }
164
165        System.out.println("first name: " + first);
166
167        //now, find another name, that will clash with the first one both in the empty and a doubled scope:
168        Scope doubledEmptyScope = WriteableScope.create(symtab.unnamedModule.unnamedPackage);
169        dble.invoke(doubledEmptyScope);
170        Integer firstNameTestScopeIndex = (Integer) getIndex.invoke(emptyScope, first);
171        Integer firstNameDoubleScopeIndex = (Integer) getIndex.invoke(doubledEmptyScope, first);
172        Name other = names.fromString("b");
173        while (!Objects.equals(firstNameTestScopeIndex, getIndex.invoke(emptyScope, other)) ||
174               !Objects.equals(firstNameDoubleScopeIndex, getIndex.invoke(doubledEmptyScope, other))) {
175            if (tries++ > MAX_TRIES) {
176                System.err.println("could not find a name that would properly clash with the first chosen name");
177                errors++;
178                return ;
179            }
180            other = names.fromString("b" + other);
181        }
182
183        System.out.println("other name: " + other);
184
185        Symbol firstSymbol = new VarSymbol(0, first, Type.noType, null);
186        Symbol otherSymbol = new VarSymbol(0, other, Type.noType, null);
187
188        //test the situation described above:
189        WriteableScope testScope1 = WriteableScope.create(symtab.unnamedModule.unnamedPackage);
190        testScope1.enter(firstSymbol);
191
192        WriteableScope dupped1 = testScope1.dup();
193
194        dupped1.enter(otherSymbol);
195        dble.invoke(dupped1);
196
197        if (testScope1.dupUnshared().findFirst(first) != firstSymbol) {
198            System.err.println("cannot find the Symbol in the dupUnshared scope (1)");
199            errors++;
200        }
201
202        //also check a situation where the clashing Symbol is removed from the dupped scope:
203        WriteableScope testScope2 = WriteableScope.create(symtab.unnamedModule.unnamedPackage);
204        testScope2.enter(firstSymbol);
205
206        WriteableScope dupped2 = testScope2.dup();
207
208        dupped2.enter(otherSymbol);
209        dble.invoke(dupped2);
210        dupped2.remove(otherSymbol);
211
212        if (testScope2.dupUnshared().findFirst(first) != firstSymbol) {
213            System.err.println("cannot find the Symbol in the dupUnshared scope (2)");
214            errors++;
215        }
216    }
217
218    int MAX_TRIES = 100; // max tries to find a hash clash before giving up.
219
220}
221