Thu, 02 Dec 2010 16:37:23 -0800
7004029: intermittent failures compiling pack200
Summary: remove "bogus" entries from star-import scopes
Reviewed-by: mcimadamore
Contributed-by: per.bothner@oracle.com
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Scope.java Mon Nov 29 14:15:36 2010 -0800 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Scope.java Thu Dec 02 16:37:23 2010 -0800 1.3 @@ -83,6 +83,10 @@ 1.4 } 1.5 }; 1.6 1.7 + /** A list of scopes to be notified if items are to be removed from this scope. 1.8 + */ 1.9 + List<Scope> listeners = List.nil(); 1.10 + 1.11 public static class ScopeCounter { 1.12 protected static final Context.Key<ScopeCounter> scopeCounterKey = 1.13 new Context.Key<ScopeCounter>(); 1.14 @@ -220,7 +224,7 @@ 1.15 int n = 0; 1.16 for (int i = oldtable.length; --i >= 0; ) { 1.17 Entry e = oldtable[i]; 1.18 - if (e != null && e != sentinel && ! e.isBogus()) { 1.19 + if (e != null && e != sentinel) { 1.20 table[getIndex(e.sym.name)] = e; 1.21 n++; 1.22 } 1.23 @@ -300,6 +304,11 @@ 1.24 } 1.25 te = te.sibling; 1.26 } 1.27 + 1.28 + // remove items from scopes that have done importAll 1.29 + for (List<Scope> l = listeners; l.nonEmpty(); l = l.tail) { 1.30 + l.head.remove(sym); 1.31 + } 1.32 } 1.33 1.34 /** Enter symbol sym in this scope if not already there. 1.35 @@ -365,7 +374,7 @@ 1.36 int h = name.hashCode(); 1.37 int i = h & hashMask; 1.38 // The expression below is always odd, so it is guaranteed 1.39 - // be be mutually prime with table.length, a power of 2. 1.40 + // to be mutually prime with table.length, a power of 2. 1.41 int x = hashMask - ((h + (h >> 16)) << 1); 1.42 int d = -1; // Index of a deleted item. 1.43 for (;;) { 1.44 @@ -495,8 +504,6 @@ 1.45 // in many cases. 1.46 return scope; 1.47 } 1.48 - 1.49 - protected boolean isBogus () { return false; } 1.50 } 1.51 1.52 public static class ImportScope extends Scope { 1.53 @@ -510,15 +517,6 @@ 1.54 return new ImportEntry(sym, shadowed, sibling, scope, origin); 1.55 } 1.56 1.57 - public Entry lookup(Name name) { 1.58 - Entry e = table[getIndex(name)]; 1.59 - if (e == null) 1.60 - return sentinel; 1.61 - while (e.isBogus()) 1.62 - e = e.shadowed; 1.63 - return e; 1.64 - } 1.65 - 1.66 static class ImportEntry extends Entry { 1.67 private Scope origin; 1.68 1.69 @@ -526,35 +524,25 @@ 1.70 super(sym, shadowed, sibling, scope); 1.71 this.origin = origin; 1.72 } 1.73 - public Entry next() { 1.74 - Entry e = super.shadowed; 1.75 - while (e.isBogus()) 1.76 - e = e.shadowed; 1.77 - return e; 1.78 - } 1.79 1.80 @Override 1.81 public Scope getOrigin() { return origin; } 1.82 + } 1.83 + } 1.84 1.85 - /** 1.86 - * Is this a bogus inner-class import? 1.87 - * An inner class {@code Outer$Inner.class} read from a class file 1.88 - * starts out in a package scope under the name {@code Outer$Inner}, 1.89 - * which (if star-imported) gets copied to the import scope. 1.90 - * When the InnerClasses attribute is processed, the ClassSymbol 1.91 - * is renamed in place (to {@code Inner}), and the owner changed 1.92 - * to the {@code Outer} class. The ImportScope still has the old 1.93 - * Entry that was created and hashed as {@code "Outer$Inner"}, 1.94 - * but whose name was changed to {@code "Inner"}. This violates 1.95 - * the invariants for the Scope hash table, and so is pretty bogus. 1.96 - * When the symbol was renamed, it should have been removed from 1.97 - * the import scope (and not just the package scope); however, 1.98 - * doing so is difficult. A better fix would be to change 1.99 - * import scopes to indirectly reference package symbols, rather 1.100 - * than copy from them. 1.101 - * Until then, we detect and skip the bogus entries using this test. 1.102 - */ 1.103 - protected boolean isBogus () { return sym.owner != scope.owner; } 1.104 + public static class StarImportScope extends ImportScope { 1.105 + 1.106 + public StarImportScope(Symbol owner) { 1.107 + super(owner); 1.108 + } 1.109 + 1.110 + public void importAll (Scope fromScope) { 1.111 + for (Scope.Entry e = fromScope.elems; e != null; e = e.sibling) { 1.112 + if (e.sym.kind == Kinds.TYP && !includes(e.sym)) 1.113 + enter(e.sym, fromScope); 1.114 + } 1.115 + // Register to be notified when imported items are removed 1.116 + fromScope.listeners = fromScope.listeners.prepend(this); 1.117 } 1.118 } 1.119
2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Enter.java Mon Nov 29 14:15:36 2010 -0800 2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Enter.java Thu Dec 02 16:37:23 2010 -0800 2.3 @@ -30,16 +30,17 @@ 2.4 import javax.tools.JavaFileManager; 2.5 2.6 import com.sun.tools.javac.code.*; 2.7 +import com.sun.tools.javac.code.Scope.*; 2.8 +import com.sun.tools.javac.code.Symbol.*; 2.9 +import com.sun.tools.javac.code.Type.*; 2.10 import com.sun.tools.javac.jvm.*; 2.11 +import com.sun.tools.javac.main.RecognizedOptions.PkgInfo; 2.12 import com.sun.tools.javac.tree.*; 2.13 +import com.sun.tools.javac.tree.JCTree.*; 2.14 import com.sun.tools.javac.util.*; 2.15 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 2.16 import com.sun.tools.javac.util.List; 2.17 2.18 -import com.sun.tools.javac.code.Type.*; 2.19 -import com.sun.tools.javac.code.Symbol.*; 2.20 -import com.sun.tools.javac.main.RecognizedOptions.PkgInfo; 2.21 -import com.sun.tools.javac.tree.JCTree.*; 2.22 2.23 import static com.sun.tools.javac.code.Flags.*; 2.24 import static com.sun.tools.javac.code.Kinds.*; 2.25 @@ -207,8 +208,8 @@ 2.26 Env<AttrContext> localEnv = new Env<AttrContext>(tree, new AttrContext()); 2.27 localEnv.toplevel = tree; 2.28 localEnv.enclClass = predefClassDef; 2.29 - tree.namedImportScope = new Scope.ImportScope(tree.packge); 2.30 - tree.starImportScope = new Scope.ImportScope(tree.packge); 2.31 + tree.namedImportScope = new ImportScope(tree.packge); 2.32 + tree.starImportScope = new StarImportScope(tree.packge); 2.33 localEnv.info.scope = tree.namedImportScope; 2.34 localEnv.info.lint = lint; 2.35 return localEnv;
3.1 --- a/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Mon Nov 29 14:15:36 2010 -0800 3.2 +++ b/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Thu Dec 02 16:37:23 2010 -0800 3.3 @@ -143,12 +143,7 @@ 3.4 log.error(pos, "doesnt.exist", tsym); 3.5 } 3.6 } 3.7 - final Scope fromScope = tsym.members(); 3.8 - final Scope toScope = env.toplevel.starImportScope; 3.9 - for (Scope.Entry e = fromScope.elems; e != null; e = e.sibling) { 3.10 - if (e.sym.kind == TYP && !toScope.includes(e.sym)) 3.11 - toScope.enter(e.sym, fromScope); 3.12 - } 3.13 + env.toplevel.starImportScope.importAll(tsym.members()); 3.14 } 3.15 3.16 /** Import all static members of a class or package on demand.
4.1 --- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java Mon Nov 29 14:15:36 2010 -0800 4.2 +++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java Thu Dec 02 16:37:23 2010 -0800 4.3 @@ -37,7 +37,7 @@ 4.4 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 4.5 import com.sun.tools.javac.util.List; 4.6 import com.sun.tools.javac.code.*; 4.7 -import com.sun.tools.javac.code.Scope; 4.8 +import com.sun.tools.javac.code.Scope.*; 4.9 import com.sun.tools.javac.code.Symbol.*; 4.10 import com.sun.source.tree.*; 4.11 4.12 @@ -434,8 +434,8 @@ 4.13 public List<JCTree> defs; 4.14 public JavaFileObject sourcefile; 4.15 public PackageSymbol packge; 4.16 - public Scope namedImportScope; 4.17 - public Scope starImportScope; 4.18 + public ImportScope namedImportScope; 4.19 + public StarImportScope starImportScope; 4.20 public long flags; 4.21 public Position.LineMap lineMap = null; 4.22 public Map<JCTree, String> docComments = null; 4.23 @@ -445,8 +445,8 @@ 4.24 List<JCTree> defs, 4.25 JavaFileObject sourcefile, 4.26 PackageSymbol packge, 4.27 - Scope namedImportScope, 4.28 - Scope starImportScope) { 4.29 + ImportScope namedImportScope, 4.30 + StarImportScope starImportScope) { 4.31 this.packageAnnotations = packageAnnotations; 4.32 this.pid = pid; 4.33 this.defs = defs;
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/tools/javac/scope/HashCollisionTest.java Thu Dec 02 16:37:23 2010 -0800 5.3 @@ -0,0 +1,251 @@ 5.4 +/* 5.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.23 + * or visit www.oracle.com if you need additional information or have any 5.24 + * questions. 5.25 + */ 5.26 + 5.27 +/* 5.28 + * @test 5.29 + * @bug 7004029 5.30 + * @summary Ensure Scope impl can cope with hash collisions 5.31 + */ 5.32 + 5.33 +import java.lang.reflect.*; 5.34 +import java.io.*; 5.35 +import com.sun.tools.javac.util.*; 5.36 +import com.sun.tools.javac.code.*; 5.37 +import com.sun.tools.javac.code.Scope.*; 5.38 +import com.sun.tools.javac.code.Symbol.*; 5.39 +import com.sun.tools.javac.file.JavacFileManager; 5.40 +import static com.sun.tools.javac.code.Kinds.*; 5.41 + 5.42 +public class HashCollisionTest { 5.43 + public static void main(String... args) throws Exception { 5.44 + new HashCollisionTest().run(); 5.45 + } 5.46 + 5.47 + void run() throws Exception { 5.48 + // set up basic environment for test 5.49 + Context context = new Context(); 5.50 + JavacFileManager.preRegister(context); // required by ClassReader which is required by Symtab 5.51 + names = Names.instance(context); // Name.Table impls tied to an instance of Names 5.52 + symtab = Symtab.instance(context); 5.53 + scopeCounter = ScopeCounter.instance(context); 5.54 + 5.55 + // determine hashMask for an empty scope 5.56 + Scope emptyScope = new Scope(symtab.unnamedPackage); // any owner will do 5.57 + Field sHashMask = Scope.class.getDeclaredField("hashMask"); 5.58 + sHashMask.setAccessible(true); 5.59 + scopeHashMask = sHashMask.getInt(emptyScope); 5.60 + log("scopeHashMask: " + scopeHashMask); 5.61 + 5.62 + // 1. determine the Name.hashCode of "Entry", and therefore the index of 5.63 + // Entry in an empty scope. i.e. name.hashCode() & Scope.hashMask 5.64 + Name entry = names.fromString("Entry"); 5.65 + 5.66 + // 2. create names of the form *$Entry until we find a name with a 5.67 + // hashcode which yields the same index as Entry in an empty scope. 5.68 + // Since Name.hashCode is a function of position (and not content) it 5.69 + // should work to create successively longer names until one with the 5.70 + // desired characteristics is found. 5.71 + Name outerName; 5.72 + Name innerName; 5.73 + StringBuilder sb = new StringBuilder("C"); 5.74 + int i = 0; 5.75 + do { 5.76 + sb.append(Integer.toString(i % 10)); 5.77 + innerName = names.fromString(sb + "$Entry"); 5.78 + } while (!clash(entry, innerName) && (++i) < MAX_TRIES); 5.79 + 5.80 + if (clash(entry, innerName)) { 5.81 + log("Detected expected hash collision for " + entry + " and " + innerName 5.82 + + " after " + i + " tries"); 5.83 + } else { 5.84 + throw new Exception("No potential collision found after " + i + " tries"); 5.85 + } 5.86 + 5.87 + outerName = names.fromString(sb.toString()); 5.88 + 5.89 + /* 5.90 + * Now we can set up the scenario. 5.91 + */ 5.92 + 5.93 + // 3. Create a nested class named Entry 5.94 + ClassSymbol cc = createClass(names.fromString("C"), symtab.unnamedPackage); 5.95 + ClassSymbol ce = createClass(entry, cc); 5.96 + 5.97 + // 4. Create a package containing a nested class using the name from 2 5.98 + PackageSymbol p = new PackageSymbol(names.fromString("p"), symtab.rootPackage); 5.99 + p.members_field = new Scope(p); 5.100 + ClassSymbol inner = createClass(innerName, p); 5.101 + // we'll need this later when we "rename" cn 5.102 + ClassSymbol outer = createClass(outerName, p); 5.103 + 5.104 + // 5. Create a star-import scope 5.105 + log ("createStarImportScope"); 5.106 + 5.107 + // if StarImportScope exists, use it, otherwise, for testing legacy code, 5.108 + // fall back on ImportScope 5.109 + Scope starImportScope; 5.110 + Method importAll; 5.111 + PackageSymbol pkg = new PackageSymbol(names.fromString("pkg"), symtab.rootPackage); 5.112 + try { 5.113 + Class<?> c = Class.forName("com.sun.tools.javac.code.Scope$StarImportScope"); 5.114 + Constructor ctor = c.getDeclaredConstructor(new Class[] { Symbol.class }); 5.115 + importAll = c.getDeclaredMethod("importAll", new Class[] { Scope.class }); 5.116 + starImportScope = (Scope) ctor.newInstance(new Object[] { pkg }); 5.117 + } catch (ClassNotFoundException e) { 5.118 + starImportScope = new ImportScope(pkg); 5.119 + importAll = null; 5.120 + } 5.121 + 5.122 + dump("initial", starImportScope); 5.123 + 5.124 + // 6. Insert the contents of the package from 4. 5.125 + Scope p_members = p.members(); 5.126 + if (importAll != null) { 5.127 + importAll.invoke(starImportScope, p_members); 5.128 + } else { 5.129 + Scope fromScope = p_members; 5.130 + Scope toScope = starImportScope; 5.131 + // The following lines are taken from MemberEnter.importAll, 5.132 + // before the use of StarImportScope.importAll. 5.133 + for (Scope.Entry e = fromScope.elems; e != null; e = e.sibling) { 5.134 + if (e.sym.kind == TYP && !toScope.includes(e.sym)) 5.135 + toScope.enter(e.sym, fromScope); 5.136 + } 5.137 + } 5.138 + 5.139 + dump("imported p", starImportScope); 5.140 + 5.141 + // 7. Insert the class from 3. 5.142 + starImportScope.enter(ce, cc.members_field); 5.143 + dump("imported ce", starImportScope); 5.144 + 5.145 + /* 5.146 + * Set the trap. 5.147 + */ 5.148 + 5.149 + // 8. Rename the nested class to Entry. so that there is a bogus entry in the star-import scope 5.150 + p.members_field.remove(inner); 5.151 + inner.name = entry; 5.152 + inner.owner = outer; 5.153 + outer.members_field.enter(inner); 5.154 + 5.155 + // 9. Lookup Entry 5.156 + Scope.Entry e = starImportScope.lookup(entry); 5.157 + dump("final", starImportScope); 5.158 + 5.159 + if (e.sym == null) 5.160 + throw new Exception("symbol not found: " + entry); 5.161 + } 5.162 + 5.163 + /* 5.164 + * Check for a (probable) hash collision in an empty scope. 5.165 + */ 5.166 + boolean clash(Name n1, Name n2) { 5.167 + log(n1 + " hc:" + n1.hashCode() + " v:" + (n1.hashCode() & scopeHashMask) + ", " + 5.168 + n2 + " hc:" + n2.hashCode() + " v:" + (n2.hashCode() & scopeHashMask)); 5.169 + return (n1.hashCode() & scopeHashMask) == (n2.hashCode() & scopeHashMask); 5.170 + } 5.171 + 5.172 + /** 5.173 + * Create a class symbol, init the members scope, and add it to owner's scope. 5.174 + */ 5.175 + ClassSymbol createClass(Name name, Symbol owner) { 5.176 + ClassSymbol sym = new ClassSymbol(0, name, owner); 5.177 + sym.members_field = new ClassScope(sym, scopeCounter); 5.178 + if (owner != symtab.unnamedPackage) 5.179 + owner.members().enter(sym); 5.180 + return sym; 5.181 + } 5.182 + 5.183 + /** 5.184 + * Dump the contents of a scope to System.err. 5.185 + */ 5.186 + void dump(String label, Scope s) throws Exception { 5.187 + dump(label, s, System.err); 5.188 + } 5.189 + 5.190 + /** 5.191 + * Dump the contents of a scope to a stream. 5.192 + */ 5.193 + void dump(String label, Scope s, PrintStream out) throws Exception { 5.194 + out.println(label); 5.195 + Field sTable = Scope.class.getDeclaredField("table"); 5.196 + sTable.setAccessible(true); 5.197 + 5.198 + out.println("owner:" + s.owner); 5.199 + Scope.Entry[] table = (Scope.Entry[]) sTable.get(s); 5.200 + for (int i = 0; i < table.length; i++) { 5.201 + if (i > 0) 5.202 + out.print(", "); 5.203 + out.print(i + ":" + toString(table[i], table, false)); 5.204 + } 5.205 + out.println(); 5.206 + } 5.207 + 5.208 + /** 5.209 + * Create a string showing the contents of an entry, using the table 5.210 + * to help identify cross-references to other entries in the table. 5.211 + * @param e the entry to be shown 5.212 + * @param table the table containing the other entries 5.213 + */ 5.214 + String toString(Scope.Entry e, Scope.Entry[] table, boolean ref) { 5.215 + if (e == null) 5.216 + return "null"; 5.217 + if (e.sym == null) 5.218 + return "sent"; // sentinel 5.219 + if (ref) { 5.220 + int index = indexOf(table, e); 5.221 + if (index != -1) 5.222 + return String.valueOf(index); 5.223 + } 5.224 + return "(" + e.sym.name + ":" + e.sym 5.225 + + ",shdw:" + toString(e.next(), table, true) 5.226 + + ",sibl:" + toString(e.sibling, table, true) 5.227 + + ((e.sym.owner != e.scope.owner) 5.228 + ? (",BOGUS[" + e.sym.owner + "," + e.scope.owner + "]") 5.229 + : "") 5.230 + + ")"; 5.231 + } 5.232 + 5.233 + <T> int indexOf(T[] array, T item) { 5.234 + for (int i = 0; i < array.length; i++) { 5.235 + if (array[i] == item) 5.236 + return i; 5.237 + } 5.238 + return -1; 5.239 + } 5.240 + 5.241 + /** 5.242 + * Write a message to stderr. 5.243 + */ 5.244 + void log(String msg) { 5.245 + System.err.println(msg); 5.246 + } 5.247 + 5.248 + int MAX_TRIES = 100; // max tries to find a hash clash before giving up. 5.249 + int scopeHashMask; 5.250 + 5.251 + Names names; 5.252 + Symtab symtab; 5.253 + ScopeCounter scopeCounter; 5.254 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/tools/javac/scope/StarImportTest.java Thu Dec 02 16:37:23 2010 -0800 6.3 @@ -0,0 +1,402 @@ 6.4 +/* 6.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.7 + * 6.8 + * This code is free software; you can redistribute it and/or modify it 6.9 + * under the terms of the GNU General Public License version 2 only, as 6.10 + * published by the Free Software Foundation. 6.11 + * 6.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 6.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.15 + * version 2 for more details (a copy is included in the LICENSE file that 6.16 + * accompanied this code). 6.17 + * 6.18 + * You should have received a copy of the GNU General Public License version 6.19 + * 2 along with this work; if not, write to the Free Software Foundation, 6.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.21 + * 6.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 6.23 + * or visit www.oracle.com if you need additional information or have any 6.24 + * questions. 6.25 + */ 6.26 + 6.27 +/* 6.28 + * @test 6.29 + * @bug 7004029 6.30 + * @summary Basher for star-import scopes 6.31 + */ 6.32 + 6.33 +import java.lang.reflect.*; 6.34 +import java.util.*; 6.35 +import java.util.List; 6.36 +import com.sun.tools.javac.util.*; 6.37 +import com.sun.tools.javac.code.*; 6.38 +import com.sun.tools.javac.code.Scope.*; 6.39 +import com.sun.tools.javac.code.Symbol.*; 6.40 +import com.sun.tools.javac.file.JavacFileManager; 6.41 +import static com.sun.tools.javac.code.Kinds.*; 6.42 + 6.43 +public class StarImportTest { 6.44 + public static void main(String... args) throws Exception { 6.45 + new StarImportTest().run(args); 6.46 + } 6.47 + 6.48 + void run(String... args) throws Exception { 6.49 + int count = 1; 6.50 + 6.51 + for (int i = 0; i < args.length; i++) { 6.52 + String arg = args[i]; 6.53 + if (arg.equals("-seed") && (i + 1 < args.length)) 6.54 + seed = Long.parseLong(args[++i]); 6.55 + else if(arg.equals("-tests") && (i + 1 < args.length)) 6.56 + count = Integer.parseInt(args[++i]); 6.57 + else 6.58 + throw new Exception("unknown arg: " + arg); 6.59 + } 6.60 + 6.61 + rgen = new Random(seed); 6.62 + 6.63 + for (int i = 0; i < count; i++) { 6.64 + Test t = new Test(); 6.65 + t.run(); 6.66 + } 6.67 + 6.68 + if (errors > 0) 6.69 + throw new Exception(errors + " errors found"); 6.70 + } 6.71 + 6.72 + /** 6.73 + * Select a random element from an array of choices. 6.74 + */ 6.75 + <T> T random(T... choices) { 6.76 + return choices[rgen.nextInt(choices.length)]; 6.77 + } 6.78 + 6.79 + /** 6.80 + * Write a message to stderr. 6.81 + */ 6.82 + void log(String msg) { 6.83 + System.err.println(msg); 6.84 + } 6.85 + 6.86 + /** 6.87 + * Write a message to stderr, and dump a scope. 6.88 + */ 6.89 + void log(String msg, Scope s) { 6.90 + System.err.print(msg); 6.91 + System.err.print(": "); 6.92 + String sep = "("; 6.93 + for (Scope.Entry se = s.elems; se != null; se = se.sibling) { 6.94 + for (Scope.Entry e = se; e.sym != null; e = e.next()) { 6.95 + System.err.print(sep + e.sym.name + ":" + e.sym); 6.96 + sep = ","; 6.97 + } 6.98 + System.err.print(")"); 6.99 + sep = ", ("; 6.100 + } 6.101 + System.err.println(); 6.102 + } 6.103 + 6.104 + /** 6.105 + * Write an error message to stderr. 6.106 + */ 6.107 + void error(String msg) { 6.108 + System.err.println("Error: " + msg); 6.109 + errors++; 6.110 + } 6.111 + 6.112 + Random rgen; 6.113 + long seed = 0; 6.114 + 6.115 + int errors; 6.116 + 6.117 + enum SetupKind { NAMES, PACKAGE, CLASS }; 6.118 + static final int MAX_SETUP_COUNT = 50; 6.119 + static final int MAX_SETUP_NAME_COUNT = 20; 6.120 + static final int MAX_SETUP_PACKAGE_COUNT = 20; 6.121 + static final int MAX_SETUP_CLASS_COUNT = 20; 6.122 + 6.123 + /** Class to encapsulate a test run. */ 6.124 + class Test { 6.125 + /** Run the test. */ 6.126 + void run() throws Exception { 6.127 + log ("starting test"); 6.128 + setup(); 6.129 + createStarImportScope(); 6.130 + test(); 6.131 + } 6.132 + 6.133 + /** 6.134 + * Setup env by creating pseudo-random collection of names, packages and classes. 6.135 + */ 6.136 + void setup() { 6.137 + log ("setup"); 6.138 + context = new Context(); 6.139 + JavacFileManager.preRegister(context); // required by ClassReader which is required by Symtab 6.140 + names = Names.instance(context); // Name.Table impls tied to an instance of Names 6.141 + symtab = Symtab.instance(context); 6.142 + scopeCounter = ScopeCounter.instance(context); 6.143 + int setupCount = rgen.nextInt(MAX_SETUP_COUNT); 6.144 + for (int i = 0; i < setupCount; i++) { 6.145 + switch (random(SetupKind.values())) { 6.146 + case NAMES: 6.147 + setupNames(); 6.148 + break; 6.149 + case PACKAGE: 6.150 + setupPackage(); 6.151 + break; 6.152 + case CLASS: 6.153 + setupClass(); 6.154 + break; 6.155 + } 6.156 + } 6.157 + } 6.158 + 6.159 + /** 6.160 + * Set up a random number of names. 6.161 + */ 6.162 + void setupNames() { 6.163 + int count = rgen.nextInt(MAX_SETUP_NAME_COUNT); 6.164 + log("setup: creating " + count + " new names"); 6.165 + for (int i = 0; i < count; i++) { 6.166 + names.fromString("n" + (++nextNameSerial)); 6.167 + } 6.168 + } 6.169 + 6.170 + /** 6.171 + * Set up a package containing a random number of member elements. 6.172 + */ 6.173 + void setupPackage() { 6.174 + Name name = names.fromString("p" + (++nextPackageSerial)); 6.175 + int count = rgen.nextInt(MAX_SETUP_PACKAGE_COUNT); 6.176 + log("setup: creating package " + name + " with " + count + " entries"); 6.177 + PackageSymbol p = new PackageSymbol(name, symtab.rootPackage); 6.178 + p.members_field = new Scope(p); 6.179 + for (int i = 0; i < count; i++) { 6.180 + String outer = name + "c" + i; 6.181 + String suffix = random(null, "$Entry", "$Entry2"); 6.182 + ClassSymbol c1 = createClass(names.fromString(outer), p); 6.183 +// log("setup: created " + c1); 6.184 + if (suffix != null) { 6.185 + ClassSymbol c2 = createClass(names.fromString(outer + suffix), p); 6.186 +// log("setup: created " + c2); 6.187 + } 6.188 + } 6.189 +// log("package " + p, p.members_field); 6.190 + packages.add(p); 6.191 + imports.add(p); 6.192 + } 6.193 + 6.194 + /** 6.195 + * Set up a class containing a random number of member elements. 6.196 + */ 6.197 + void setupClass() { 6.198 + Name name = names.fromString("c" + (++nextClassSerial)); 6.199 + int count = rgen.nextInt(MAX_SETUP_CLASS_COUNT); 6.200 + log("setup: creating class " + name + " with " + count + " entries"); 6.201 + ClassSymbol c = createClass(name, symtab.unnamedPackage); 6.202 +// log("setup: created " + c); 6.203 + for (int i = 0; i < count; i++) { 6.204 + ClassSymbol ic = createClass(names.fromString("Entry" + i), c); 6.205 +// log("setup: created " + ic); 6.206 + } 6.207 + classes.add(c); 6.208 + imports.add(c); 6.209 + } 6.210 + 6.211 + /** 6.212 + * Create a star-import scope and a model therof, from the packages and 6.213 + * classes created by setupPackages and setupClasses. 6.214 + * @throws Exception for fatal errors, such as from reflection 6.215 + */ 6.216 + void createStarImportScope() throws Exception { 6.217 + log ("createStarImportScope"); 6.218 + PackageSymbol pkg = new PackageSymbol(names.fromString("pkg"), symtab.rootPackage); 6.219 + 6.220 + // if StarImportScope exists, use it, otherwise, for testing legacy code, 6.221 + // fall back on ImportScope 6.222 + Method importAll; 6.223 + try { 6.224 + Class<?> c = Class.forName("com.sun.tools.javac.code.Scope$StarImportScope"); 6.225 + Constructor ctor = c.getDeclaredConstructor(new Class[] { Symbol.class }); 6.226 + importAll = c.getDeclaredMethod("importAll", new Class[] { Scope.class }); 6.227 + starImportScope = (Scope) ctor.newInstance(new Object[] { pkg }); 6.228 + } catch (ClassNotFoundException e) { 6.229 + starImportScope = new ImportScope(pkg); 6.230 + importAll = null; 6.231 + } 6.232 + starImportModel = new Model(); 6.233 + 6.234 + for (Symbol imp: imports) { 6.235 + Scope members = imp.members(); 6.236 + if (importAll != null) { 6.237 +// log("importAll", members); 6.238 + importAll.invoke(starImportScope, members); 6.239 + } else { 6.240 + Scope fromScope = members; 6.241 + Scope toScope = starImportScope; 6.242 + // The following lines are taken from MemberEnter.importAll, 6.243 + // before the use of StarImportScope.importAll. 6.244 + for (Scope.Entry e = fromScope.elems; e != null; e = e.sibling) { 6.245 + if (e.sym.kind == TYP && !toScope.includes(e.sym)) 6.246 + toScope.enter(e.sym, fromScope); 6.247 + } 6.248 + } 6.249 + 6.250 + for (Scope.Entry e = members.elems; e != null; e = e.sibling) { 6.251 + starImportModel.enter(e.sym); 6.252 + } 6.253 + } 6.254 + 6.255 +// log("star-import scope", starImportScope); 6.256 + starImportModel.check(starImportScope); 6.257 + } 6.258 + 6.259 + /** 6.260 + * The core of the test. In a random order, move nested classes from 6.261 + * the package in which they created to the class which should own them. 6.262 + */ 6.263 + void test() { 6.264 + log ("test"); 6.265 + List<ClassSymbol> nestedClasses = new LinkedList<ClassSymbol>(); 6.266 + for (PackageSymbol p: packages) { 6.267 + for (Scope.Entry se = p.members_field.elems; se != null; se = se.sibling) { 6.268 + if (se.sym.name.toString().contains("$")) 6.269 + nestedClasses.add((ClassSymbol) se.sym); 6.270 + } 6.271 + } 6.272 + 6.273 + for (int i = nestedClasses.size(); i > 0; i--) { 6.274 + // select a random nested class to move from package to class 6.275 + ClassSymbol sym = nestedClasses.remove(rgen.nextInt(i)); 6.276 + log("adjusting class " + sym); 6.277 + 6.278 + // remove from star import model 6.279 + starImportModel.remove(sym); 6.280 + 6.281 + String s = sym.name.toString(); 6.282 + int dollar = s.indexOf("$"); 6.283 + 6.284 + // owner should be a package 6.285 + assert (sym.owner.kind == PCK); 6.286 + 6.287 + // determine new owner 6.288 + Name outerName = names.fromString(s.substring(0, dollar)); 6.289 +// log(sym + " owner: " + sym.owner, sym.owner.members()); 6.290 + Scope.Entry outerEntry = sym.owner.members().lookup(outerName); 6.291 + ClassSymbol outer = (ClassSymbol) outerEntry.sym; 6.292 +// log("outer: " + outerName + " " + outer); 6.293 + 6.294 + // remove from package 6.295 + sym.owner.members().remove(sym); 6.296 + 6.297 + // rename and insert into class 6.298 + sym.name = names.fromString(s.substring(dollar + 1)); 6.299 + outer.members().enter(sym); 6.300 + sym.owner = outer; 6.301 + 6.302 + // verify 6.303 + starImportModel.check(starImportScope); 6.304 + } 6.305 + } 6.306 + 6.307 + ClassSymbol createClass(Name name, Symbol owner) { 6.308 + ClassSymbol sym = new ClassSymbol(0, name, owner); 6.309 + sym.members_field = new ClassScope(sym, scopeCounter); 6.310 + if (owner != symtab.unnamedPackage) 6.311 + owner.members().enter(sym); 6.312 + return sym; 6.313 + } 6.314 + 6.315 + Context context; 6.316 + Symtab symtab; 6.317 + ScopeCounter scopeCounter; 6.318 + Names names; 6.319 + int nextNameSerial; 6.320 + List<PackageSymbol> packages = new ArrayList<PackageSymbol>(); 6.321 + int nextPackageSerial; 6.322 + List<ClassSymbol> classes = new ArrayList<ClassSymbol>(); 6.323 + List<Symbol> imports = new ArrayList<Symbol>(); 6.324 + int nextClassSerial; 6.325 + 6.326 + Scope starImportScope; 6.327 + Model starImportModel; 6.328 + } 6.329 + 6.330 + class Model { 6.331 + private Map<Name, Set<Symbol>> map = new HashMap<Name, Set<Symbol>>(); 6.332 + private Set<Symbol> bogus = new HashSet<Symbol>(); 6.333 + 6.334 + void enter(Symbol sym) { 6.335 + Set<Symbol> syms = map.get(sym.name); 6.336 + if (syms == null) 6.337 + map.put(sym.name, syms = new LinkedHashSet<Symbol>()); 6.338 + syms.add(sym); 6.339 + } 6.340 + 6.341 + void remove(Symbol sym) { 6.342 + Set<Symbol> syms = map.get(sym.name); 6.343 + if (syms == null) 6.344 + error("no entries for " + sym.name + " found in reference model"); 6.345 + else { 6.346 + boolean ok = syms.remove(sym); 6.347 + if (ok) { 6.348 +// log(sym.name + "(" + sym + ") removed from reference model"); 6.349 + } else { 6.350 + error(sym.name + " not found in reference model"); 6.351 + } 6.352 + if (syms.isEmpty()) 6.353 + map.remove(sym.name); 6.354 + } 6.355 + } 6.356 + 6.357 + /** 6.358 + * Check the contents of a scope 6.359 + */ 6.360 + void check(Scope scope) { 6.361 + // First, check all entries in scope are in map 6.362 + int bogusCount = 0; 6.363 + for (Scope.Entry se = scope.elems; se != null; se = se.sibling) { 6.364 + Symbol sym = se.sym; 6.365 + if (sym.owner != se.scope.owner) { 6.366 + if (bogus.contains(sym)) { 6.367 + bogusCount++; 6.368 + } else { 6.369 + log("Warning: " + sym.name + ":" + sym + " appears to be bogus"); 6.370 + bogus.add(sym); 6.371 + } 6.372 + } else { 6.373 + Set<Symbol> syms = map.get(sym.name); 6.374 + if (syms == null) { 6.375 + error("check: no entries found for " + sym.name + ":" + sym + " in reference map"); 6.376 + } else if (!syms.contains(sym)) { 6.377 + error("check: symbol " + sym.name + ":" + sym + " not found in reference map"); 6.378 + } 6.379 + } 6.380 + } 6.381 + if (bogusCount > 0) { 6.382 + log("Warning: " + bogusCount + " other bogus entries previously reported"); 6.383 + } 6.384 + 6.385 + // Second, check all entries in map are in scope 6.386 + for (Map.Entry<Name,Set<Symbol>> me: map.entrySet()) { 6.387 + Name name = me.getKey(); 6.388 + Scope.Entry se = scope.lookup(name); 6.389 + assert (se != null); 6.390 + if (se.sym == null) { 6.391 + error("check: no entries found for " + name + " in scope"); 6.392 + continue; 6.393 + } 6.394 + nextSym: 6.395 + for (Symbol sym: me.getValue()) { 6.396 + for (Scope.Entry e = se; e.sym != null; e = e.next()) { 6.397 + if (sym == e.sym) 6.398 + continue nextSym; 6.399 + } 6.400 + error("check: symbol " + sym + " not found in scope"); 6.401 + } 6.402 + } 6.403 + } 6.404 + } 6.405 +}