Mon, 17 Oct 2011 12:57:36 +0100
7093325: Redundant entry in bytecode exception table
Summary: Inlining of finalizers does not update gaps list accordingly
Reviewed-by: jjg
jjg@767 | 1 | /* |
ohair@962 | 2 | * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. |
jjg@767 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jjg@767 | 4 | * |
jjg@767 | 5 | * This code is free software; you can redistribute it and/or modify it |
jjg@767 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jjg@767 | 7 | * published by the Free Software Foundation. |
jjg@767 | 8 | * |
jjg@767 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jjg@767 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jjg@767 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jjg@767 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
jjg@767 | 13 | * accompanied this code). |
jjg@767 | 14 | * |
jjg@767 | 15 | * You should have received a copy of the GNU General Public License version |
jjg@767 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
jjg@767 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jjg@767 | 18 | * |
jjg@767 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
jjg@767 | 20 | * or visit www.oracle.com if you need additional information or have any |
jjg@767 | 21 | * questions. |
jjg@767 | 22 | */ |
jjg@767 | 23 | |
jjg@767 | 24 | /* |
jjg@767 | 25 | * @test |
jjg@767 | 26 | * @bug 7004029 |
jjg@767 | 27 | * @summary Ensure Scope impl can cope with hash collisions |
jjg@767 | 28 | */ |
jjg@767 | 29 | |
jjg@767 | 30 | import java.lang.reflect.*; |
jjg@767 | 31 | import java.io.*; |
jjg@767 | 32 | import com.sun.tools.javac.util.*; |
jjg@767 | 33 | import com.sun.tools.javac.code.*; |
jjg@767 | 34 | import com.sun.tools.javac.code.Scope.*; |
jjg@767 | 35 | import com.sun.tools.javac.code.Symbol.*; |
jjg@767 | 36 | import com.sun.tools.javac.file.JavacFileManager; |
jjg@767 | 37 | import static com.sun.tools.javac.code.Kinds.*; |
jjg@767 | 38 | |
jjg@767 | 39 | public class HashCollisionTest { |
jjg@767 | 40 | public static void main(String... args) throws Exception { |
jjg@767 | 41 | new HashCollisionTest().run(); |
jjg@767 | 42 | } |
jjg@767 | 43 | |
jjg@767 | 44 | void run() throws Exception { |
jjg@767 | 45 | // set up basic environment for test |
jjg@767 | 46 | Context context = new Context(); |
jjg@767 | 47 | JavacFileManager.preRegister(context); // required by ClassReader which is required by Symtab |
jjg@767 | 48 | names = Names.instance(context); // Name.Table impls tied to an instance of Names |
jjg@767 | 49 | symtab = Symtab.instance(context); |
jjg@767 | 50 | |
jjg@767 | 51 | // determine hashMask for an empty scope |
jjg@767 | 52 | Scope emptyScope = new Scope(symtab.unnamedPackage); // any owner will do |
jjg@767 | 53 | Field sHashMask = Scope.class.getDeclaredField("hashMask"); |
jjg@767 | 54 | sHashMask.setAccessible(true); |
jjg@767 | 55 | scopeHashMask = sHashMask.getInt(emptyScope); |
jjg@767 | 56 | log("scopeHashMask: " + scopeHashMask); |
jjg@767 | 57 | |
jjg@767 | 58 | // 1. determine the Name.hashCode of "Entry", and therefore the index of |
jjg@767 | 59 | // Entry in an empty scope. i.e. name.hashCode() & Scope.hashMask |
jjg@767 | 60 | Name entry = names.fromString("Entry"); |
jjg@767 | 61 | |
jjg@767 | 62 | // 2. create names of the form *$Entry until we find a name with a |
jjg@767 | 63 | // hashcode which yields the same index as Entry in an empty scope. |
jjg@767 | 64 | // Since Name.hashCode is a function of position (and not content) it |
jjg@767 | 65 | // should work to create successively longer names until one with the |
jjg@767 | 66 | // desired characteristics is found. |
jjg@767 | 67 | Name outerName; |
jjg@767 | 68 | Name innerName; |
jjg@767 | 69 | StringBuilder sb = new StringBuilder("C"); |
jjg@767 | 70 | int i = 0; |
jjg@767 | 71 | do { |
jjg@767 | 72 | sb.append(Integer.toString(i % 10)); |
jjg@767 | 73 | innerName = names.fromString(sb + "$Entry"); |
jjg@767 | 74 | } while (!clash(entry, innerName) && (++i) < MAX_TRIES); |
jjg@767 | 75 | |
jjg@767 | 76 | if (clash(entry, innerName)) { |
jjg@767 | 77 | log("Detected expected hash collision for " + entry + " and " + innerName |
jjg@767 | 78 | + " after " + i + " tries"); |
jjg@767 | 79 | } else { |
jjg@767 | 80 | throw new Exception("No potential collision found after " + i + " tries"); |
jjg@767 | 81 | } |
jjg@767 | 82 | |
jjg@767 | 83 | outerName = names.fromString(sb.toString()); |
jjg@767 | 84 | |
jjg@767 | 85 | /* |
jjg@767 | 86 | * Now we can set up the scenario. |
jjg@767 | 87 | */ |
jjg@767 | 88 | |
jjg@767 | 89 | // 3. Create a nested class named Entry |
jjg@767 | 90 | ClassSymbol cc = createClass(names.fromString("C"), symtab.unnamedPackage); |
jjg@767 | 91 | ClassSymbol ce = createClass(entry, cc); |
jjg@767 | 92 | |
jjg@767 | 93 | // 4. Create a package containing a nested class using the name from 2 |
jjg@767 | 94 | PackageSymbol p = new PackageSymbol(names.fromString("p"), symtab.rootPackage); |
jjg@767 | 95 | p.members_field = new Scope(p); |
jjg@767 | 96 | ClassSymbol inner = createClass(innerName, p); |
jjg@767 | 97 | // we'll need this later when we "rename" cn |
jjg@767 | 98 | ClassSymbol outer = createClass(outerName, p); |
jjg@767 | 99 | |
jjg@767 | 100 | // 5. Create a star-import scope |
jjg@767 | 101 | log ("createStarImportScope"); |
jjg@767 | 102 | |
jjg@767 | 103 | // if StarImportScope exists, use it, otherwise, for testing legacy code, |
jjg@767 | 104 | // fall back on ImportScope |
jjg@767 | 105 | Scope starImportScope; |
jjg@767 | 106 | Method importAll; |
jjg@767 | 107 | PackageSymbol pkg = new PackageSymbol(names.fromString("pkg"), symtab.rootPackage); |
jjg@767 | 108 | try { |
jjg@767 | 109 | Class<?> c = Class.forName("com.sun.tools.javac.code.Scope$StarImportScope"); |
jjg@767 | 110 | Constructor ctor = c.getDeclaredConstructor(new Class[] { Symbol.class }); |
jjg@767 | 111 | importAll = c.getDeclaredMethod("importAll", new Class[] { Scope.class }); |
jjg@767 | 112 | starImportScope = (Scope) ctor.newInstance(new Object[] { pkg }); |
jjg@767 | 113 | } catch (ClassNotFoundException e) { |
jjg@767 | 114 | starImportScope = new ImportScope(pkg); |
jjg@767 | 115 | importAll = null; |
jjg@767 | 116 | } |
jjg@767 | 117 | |
jjg@767 | 118 | dump("initial", starImportScope); |
jjg@767 | 119 | |
jjg@767 | 120 | // 6. Insert the contents of the package from 4. |
jjg@767 | 121 | Scope p_members = p.members(); |
jjg@767 | 122 | if (importAll != null) { |
jjg@767 | 123 | importAll.invoke(starImportScope, p_members); |
jjg@767 | 124 | } else { |
jjg@767 | 125 | Scope fromScope = p_members; |
jjg@767 | 126 | Scope toScope = starImportScope; |
jjg@767 | 127 | // The following lines are taken from MemberEnter.importAll, |
jjg@767 | 128 | // before the use of StarImportScope.importAll. |
jjg@767 | 129 | for (Scope.Entry e = fromScope.elems; e != null; e = e.sibling) { |
jjg@767 | 130 | if (e.sym.kind == TYP && !toScope.includes(e.sym)) |
jjg@767 | 131 | toScope.enter(e.sym, fromScope); |
jjg@767 | 132 | } |
jjg@767 | 133 | } |
jjg@767 | 134 | |
jjg@767 | 135 | dump("imported p", starImportScope); |
jjg@767 | 136 | |
jjg@767 | 137 | // 7. Insert the class from 3. |
jjg@767 | 138 | starImportScope.enter(ce, cc.members_field); |
jjg@767 | 139 | dump("imported ce", starImportScope); |
jjg@767 | 140 | |
jjg@767 | 141 | /* |
jjg@767 | 142 | * Set the trap. |
jjg@767 | 143 | */ |
jjg@767 | 144 | |
jjg@767 | 145 | // 8. Rename the nested class to Entry. so that there is a bogus entry in the star-import scope |
jjg@767 | 146 | p.members_field.remove(inner); |
jjg@767 | 147 | inner.name = entry; |
jjg@767 | 148 | inner.owner = outer; |
jjg@767 | 149 | outer.members_field.enter(inner); |
jjg@767 | 150 | |
jjg@767 | 151 | // 9. Lookup Entry |
jjg@767 | 152 | Scope.Entry e = starImportScope.lookup(entry); |
jjg@767 | 153 | dump("final", starImportScope); |
jjg@767 | 154 | |
jjg@767 | 155 | if (e.sym == null) |
jjg@767 | 156 | throw new Exception("symbol not found: " + entry); |
jjg@767 | 157 | } |
jjg@767 | 158 | |
jjg@767 | 159 | /* |
jjg@767 | 160 | * Check for a (probable) hash collision in an empty scope. |
jjg@767 | 161 | */ |
jjg@767 | 162 | boolean clash(Name n1, Name n2) { |
jjg@767 | 163 | log(n1 + " hc:" + n1.hashCode() + " v:" + (n1.hashCode() & scopeHashMask) + ", " + |
jjg@767 | 164 | n2 + " hc:" + n2.hashCode() + " v:" + (n2.hashCode() & scopeHashMask)); |
jjg@767 | 165 | return (n1.hashCode() & scopeHashMask) == (n2.hashCode() & scopeHashMask); |
jjg@767 | 166 | } |
jjg@767 | 167 | |
jjg@767 | 168 | /** |
jjg@767 | 169 | * Create a class symbol, init the members scope, and add it to owner's scope. |
jjg@767 | 170 | */ |
jjg@767 | 171 | ClassSymbol createClass(Name name, Symbol owner) { |
jjg@767 | 172 | ClassSymbol sym = new ClassSymbol(0, name, owner); |
mcimadamore@858 | 173 | sym.members_field = new Scope(sym); |
jjg@767 | 174 | if (owner != symtab.unnamedPackage) |
jjg@767 | 175 | owner.members().enter(sym); |
jjg@767 | 176 | return sym; |
jjg@767 | 177 | } |
jjg@767 | 178 | |
jjg@767 | 179 | /** |
jjg@767 | 180 | * Dump the contents of a scope to System.err. |
jjg@767 | 181 | */ |
jjg@767 | 182 | void dump(String label, Scope s) throws Exception { |
jjg@767 | 183 | dump(label, s, System.err); |
jjg@767 | 184 | } |
jjg@767 | 185 | |
jjg@767 | 186 | /** |
jjg@767 | 187 | * Dump the contents of a scope to a stream. |
jjg@767 | 188 | */ |
jjg@767 | 189 | void dump(String label, Scope s, PrintStream out) throws Exception { |
jjg@767 | 190 | out.println(label); |
jjg@767 | 191 | Field sTable = Scope.class.getDeclaredField("table"); |
jjg@767 | 192 | sTable.setAccessible(true); |
jjg@767 | 193 | |
jjg@767 | 194 | out.println("owner:" + s.owner); |
jjg@767 | 195 | Scope.Entry[] table = (Scope.Entry[]) sTable.get(s); |
jjg@767 | 196 | for (int i = 0; i < table.length; i++) { |
jjg@767 | 197 | if (i > 0) |
jjg@767 | 198 | out.print(", "); |
jjg@767 | 199 | out.print(i + ":" + toString(table[i], table, false)); |
jjg@767 | 200 | } |
jjg@767 | 201 | out.println(); |
jjg@767 | 202 | } |
jjg@767 | 203 | |
jjg@767 | 204 | /** |
jjg@767 | 205 | * Create a string showing the contents of an entry, using the table |
jjg@767 | 206 | * to help identify cross-references to other entries in the table. |
jjg@767 | 207 | * @param e the entry to be shown |
jjg@767 | 208 | * @param table the table containing the other entries |
jjg@767 | 209 | */ |
jjg@767 | 210 | String toString(Scope.Entry e, Scope.Entry[] table, boolean ref) { |
jjg@767 | 211 | if (e == null) |
jjg@767 | 212 | return "null"; |
jjg@767 | 213 | if (e.sym == null) |
jjg@767 | 214 | return "sent"; // sentinel |
jjg@767 | 215 | if (ref) { |
jjg@767 | 216 | int index = indexOf(table, e); |
jjg@767 | 217 | if (index != -1) |
jjg@767 | 218 | return String.valueOf(index); |
jjg@767 | 219 | } |
jjg@767 | 220 | return "(" + e.sym.name + ":" + e.sym |
jjg@767 | 221 | + ",shdw:" + toString(e.next(), table, true) |
jjg@767 | 222 | + ",sibl:" + toString(e.sibling, table, true) |
jjg@767 | 223 | + ((e.sym.owner != e.scope.owner) |
jjg@767 | 224 | ? (",BOGUS[" + e.sym.owner + "," + e.scope.owner + "]") |
jjg@767 | 225 | : "") |
jjg@767 | 226 | + ")"; |
jjg@767 | 227 | } |
jjg@767 | 228 | |
jjg@767 | 229 | <T> int indexOf(T[] array, T item) { |
jjg@767 | 230 | for (int i = 0; i < array.length; i++) { |
jjg@767 | 231 | if (array[i] == item) |
jjg@767 | 232 | return i; |
jjg@767 | 233 | } |
jjg@767 | 234 | return -1; |
jjg@767 | 235 | } |
jjg@767 | 236 | |
jjg@767 | 237 | /** |
jjg@767 | 238 | * Write a message to stderr. |
jjg@767 | 239 | */ |
jjg@767 | 240 | void log(String msg) { |
jjg@767 | 241 | System.err.println(msg); |
jjg@767 | 242 | } |
jjg@767 | 243 | |
jjg@767 | 244 | int MAX_TRIES = 100; // max tries to find a hash clash before giving up. |
jjg@767 | 245 | int scopeHashMask; |
jjg@767 | 246 | |
jjg@767 | 247 | Names names; |
jjg@767 | 248 | Symtab symtab; |
jjg@767 | 249 | } |