7004029: intermittent failures compiling pack200

Thu, 02 Dec 2010 16:37:23 -0800

author
jjg
date
Thu, 02 Dec 2010 16:37:23 -0800
changeset 767
7e3e9f6d013f
parent 757
c44234f680da
child 768
28566c763dad

7004029: intermittent failures compiling pack200
Summary: remove "bogus" entries from star-import scopes
Reviewed-by: mcimadamore
Contributed-by: per.bothner@oracle.com

src/share/classes/com/sun/tools/javac/code/Scope.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Enter.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/MemberEnter.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/tree/JCTree.java file | annotate | diff | comparison | revisions
test/tools/javac/scope/HashCollisionTest.java file | annotate | diff | comparison | revisions
test/tools/javac/scope/StarImportTest.java file | annotate | diff | comparison | revisions
     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 +}

mercurial