Mon, 23 May 2011 11:55:55 +0100
7046348: Regression: javac complains of missing classfile for a seemingly unrelated interface
Summary: Types.implementation forces unnecessary symbol completion on superinterfaces of a given type
Reviewed-by: jjg
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Fri May 20 16:04:23 2011 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Mon May 23 11:55:55 2011 +0100 1.3 @@ -729,10 +729,6 @@ 1.4 */ 1.5 public Pool pool; 1.6 1.7 - /** members closure cache (set by Types.membersClosure) 1.8 - */ 1.9 - Scope.CompoundScope membersClosure; 1.10 - 1.11 public ClassSymbol(long flags, Name name, Type type, Symbol owner) { 1.12 super(flags, name, type, owner); 1.13 this.members_field = null;
2.1 --- a/src/share/classes/com/sun/tools/javac/code/Types.java Fri May 20 16:04:23 2011 -0700 2.2 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Mon May 23 11:55:55 2011 +0100 2.3 @@ -2061,7 +2061,7 @@ 2.4 _map.put(ms, new SoftReference<Map<TypeSymbol, Entry>>(cache)); 2.5 } 2.6 Entry e = cache.get(origin); 2.7 - CompoundScope members = membersClosure(origin.type); 2.8 + CompoundScope members = membersClosure(origin.type, true); 2.9 if (e == null || 2.10 !e.matches(implFilter, checkResult, members.getMark())) { 2.11 MethodSymbol impl = implementationInternal(ms, origin, checkResult, implFilter); 2.12 @@ -2098,36 +2098,61 @@ 2.13 // </editor-fold> 2.14 2.15 // <editor-fold defaultstate="collapsed" desc="compute transitive closure of all members in given site"> 2.16 - public CompoundScope membersClosure(Type site) { 2.17 - return membersClosure.visit(site); 2.18 - } 2.19 - 2.20 - UnaryVisitor<CompoundScope> membersClosure = new UnaryVisitor<CompoundScope>() { 2.21 - 2.22 - public CompoundScope visitType(Type t, Void s) { 2.23 + class MembersClosureCache extends SimpleVisitor<CompoundScope, Boolean> { 2.24 + 2.25 + private WeakHashMap<TypeSymbol, Entry> _map = 2.26 + new WeakHashMap<TypeSymbol, Entry>(); 2.27 + 2.28 + class Entry { 2.29 + final boolean skipInterfaces; 2.30 + final CompoundScope compoundScope; 2.31 + 2.32 + public Entry(boolean skipInterfaces, CompoundScope compoundScope) { 2.33 + this.skipInterfaces = skipInterfaces; 2.34 + this.compoundScope = compoundScope; 2.35 + } 2.36 + 2.37 + boolean matches(boolean skipInterfaces) { 2.38 + return this.skipInterfaces == skipInterfaces; 2.39 + } 2.40 + } 2.41 + 2.42 + /** members closure visitor methods **/ 2.43 + 2.44 + public CompoundScope visitType(Type t, Boolean skipInterface) { 2.45 return null; 2.46 } 2.47 2.48 @Override 2.49 - public CompoundScope visitClassType(ClassType t, Void s) { 2.50 + public CompoundScope visitClassType(ClassType t, Boolean skipInterface) { 2.51 ClassSymbol csym = (ClassSymbol)t.tsym; 2.52 - if (csym.membersClosure == null) { 2.53 + Entry e = _map.get(csym); 2.54 + if (e == null || !e.matches(skipInterface)) { 2.55 CompoundScope membersClosure = new CompoundScope(csym); 2.56 - for (Type i : interfaces(t)) { 2.57 - membersClosure.addSubScope(visit(i)); 2.58 + if (!skipInterface) { 2.59 + for (Type i : interfaces(t)) { 2.60 + membersClosure.addSubScope(visit(i, skipInterface)); 2.61 + } 2.62 } 2.63 - membersClosure.addSubScope(visit(supertype(t))); 2.64 + membersClosure.addSubScope(visit(supertype(t), skipInterface)); 2.65 membersClosure.addSubScope(csym.members()); 2.66 - csym.membersClosure = membersClosure; 2.67 + e = new Entry(skipInterface, membersClosure); 2.68 + _map.put(csym, e); 2.69 } 2.70 - return csym.membersClosure; 2.71 + return e.compoundScope; 2.72 } 2.73 2.74 @Override 2.75 - public CompoundScope visitTypeVar(TypeVar t, Void s) { 2.76 - return visit(t.getUpperBound()); 2.77 + public CompoundScope visitTypeVar(TypeVar t, Boolean skipInterface) { 2.78 + return visit(t.getUpperBound(), skipInterface); 2.79 } 2.80 - }; 2.81 + } 2.82 + 2.83 + private MembersClosureCache membersCache = new MembersClosureCache(); 2.84 + 2.85 + public CompoundScope membersClosure(Type site, boolean skipInterface) { 2.86 + return membersCache.visit(site, skipInterface); 2.87 + } 2.88 // </editor-fold> 2.89 2.90 /**
3.1 --- a/src/share/classes/com/sun/tools/javac/comp/Check.java Fri May 20 16:04:23 2011 -0700 3.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Check.java Mon May 23 11:55:55 2011 +0100 3.3 @@ -2098,10 +2098,10 @@ 3.4 void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) { 3.5 ClashFilter cf = new ClashFilter(site); 3.6 //for each method m1 that is a member of 'site'... 3.7 - for (Symbol s1 : types.membersClosure(site).getElementsByName(sym.name, cf)) { 3.8 + for (Symbol s1 : types.membersClosure(site, false).getElementsByName(sym.name, cf)) { 3.9 //...find another method m2 that is overridden (directly or indirectly) 3.10 //by method 'sym' in 'site' 3.11 - for (Symbol s2 : types.membersClosure(site).getElementsByName(sym.name, cf)) { 3.12 + for (Symbol s2 : types.membersClosure(site, false).getElementsByName(sym.name, cf)) { 3.13 if (s1 == s2 || !sym.overrides(s2, site.tsym, types, false)) continue; 3.14 //if (i) the signature of 'sym' is not a subsignature of m1 (seen as 3.15 //a member of 'site') and (ii) m1 has the same erasure as m2, issue an error 3.16 @@ -2134,7 +2134,7 @@ 3.17 void checkHideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) { 3.18 ClashFilter cf = new ClashFilter(site); 3.19 //for each method m1 that is a member of 'site'... 3.20 - for (Symbol s : types.membersClosure(site).getElementsByName(sym.name, cf)) { 3.21 + for (Symbol s : types.membersClosure(site, true).getElementsByName(sym.name, cf)) { 3.22 //if (i) the signature of 'sym' is not a subsignature of m1 (seen as 3.23 //a member of 'site') and (ii) 'sym' has the same erasure as m1, issue an error 3.24 if (!types.isSubSignature(sym.type, types.memberType(site, s), false) &&
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/tools/javac/scope/7046348/EagerInterfaceCompletionTest.java Mon May 23 11:55:55 2011 +0100 4.3 @@ -0,0 +1,206 @@ 4.4 +/* 4.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + */ 4.26 + 4.27 +/** 4.28 + * @test 4.29 + * @bug 7046348 4.30 + * @summary Regression: javac complains of missing classfile for a seemingly unrelated interface 4.31 + */ 4.32 + 4.33 +import java.io.File; 4.34 +import java.net.URI; 4.35 +import java.util.Arrays; 4.36 + 4.37 +import javax.tools.Diagnostic; 4.38 +import javax.tools.DiagnosticListener; 4.39 +import javax.tools.JavaCompiler; 4.40 +import javax.tools.JavaCompiler.CompilationTask; 4.41 +import javax.tools.JavaFileObject; 4.42 +import javax.tools.SimpleJavaFileObject; 4.43 +import javax.tools.ToolProvider; 4.44 + 4.45 +public class EagerInterfaceCompletionTest { 4.46 + 4.47 + JavaCompiler javacTool; 4.48 + File testDir; 4.49 + HierarchyKind hierarchyKind; 4.50 + TestKind testKind; 4.51 + ActionKind actionKind; 4.52 + 4.53 + EagerInterfaceCompletionTest(JavaCompiler javacTool, File testDir, 4.54 + HierarchyKind hierarchyKind, TestKind testKind, ActionKind actionKind) { 4.55 + this.javacTool = javacTool; 4.56 + this.hierarchyKind = hierarchyKind; 4.57 + this.testDir = testDir; 4.58 + this.testKind = testKind; 4.59 + this.actionKind = actionKind; 4.60 + } 4.61 + 4.62 + void test() { 4.63 + testDir.mkdirs(); 4.64 + compile(null, hierarchyKind.source); 4.65 + actionKind.doAction(this); 4.66 + DiagnosticChecker dc = new DiagnosticChecker(); 4.67 + compile(dc, testKind.source); 4.68 + if (testKind.completionFailure(actionKind, hierarchyKind) != dc.errorFound) { 4.69 + if (dc.errorFound) { 4.70 + error("Unexpected completion failure" + 4.71 + "\nhierarhcyKind " + hierarchyKind + 4.72 + "\ntestKind " + testKind + 4.73 + "\nactionKind " + actionKind); 4.74 + } else { 4.75 + error("Missing completion failure " + 4.76 + "\nhierarhcyKind " + hierarchyKind + 4.77 + "\ntestKind " + testKind + 4.78 + "\nactionKind " + actionKind); 4.79 + } 4.80 + } 4.81 + } 4.82 + 4.83 + void compile(DiagnosticChecker dc, JavaSource... sources) { 4.84 + try { 4.85 + CompilationTask ct = javacTool.getTask(null, null, dc, 4.86 + Arrays.asList("-d", testDir.getAbsolutePath(), "-cp", testDir.getAbsolutePath()), 4.87 + null, Arrays.asList(sources)); 4.88 + ct.call(); 4.89 + } 4.90 + catch (Exception e) { 4.91 + e.printStackTrace(); 4.92 + error("Internal compilation error"); 4.93 + } 4.94 + } 4.95 + 4.96 + void removeClass(String classToRemoveStr) { 4.97 + File classToRemove = new File(testDir, classToRemoveStr); 4.98 + if (!classToRemove.exists()) { 4.99 + error("Expected file " + classToRemove + " does not exists in folder " + testDir); 4.100 + } 4.101 + classToRemove.delete(); 4.102 + }; 4.103 + 4.104 + void error(String msg) { 4.105 + System.err.println(msg); 4.106 + nerrors++; 4.107 + } 4.108 + 4.109 + class DiagnosticChecker implements DiagnosticListener<JavaFileObject> { 4.110 + 4.111 + boolean errorFound = false; 4.112 + 4.113 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 4.114 + errorFound = true; 4.115 + } 4.116 + } 4.117 + 4.118 + //global declarations 4.119 + 4.120 + enum HierarchyKind { 4.121 + INTERFACE("interface A { boolean f = false; void m(); }\n" + 4.122 + "class B implements A { public void m() {} }"), 4.123 + CLASS("class A { boolean f; void m() {} }\n" + 4.124 + "class B extends A { void m() {} }"), 4.125 + ABSTRACT_CLASS("abstract class A { boolean f; abstract void m(); }\n" + 4.126 + "class B extends A { void m() {} }"); 4.127 + 4.128 + JavaSource source; 4.129 + 4.130 + private HierarchyKind(String code) { 4.131 + this.source = new JavaSource("Test1.java", code); 4.132 + } 4.133 + } 4.134 + 4.135 + enum ActionKind { 4.136 + REMOVE_A("A.class"), 4.137 + REMOVE_B("B.class"); 4.138 + 4.139 + String classFile; 4.140 + 4.141 + private ActionKind(String classFile) { 4.142 + this.classFile = classFile; 4.143 + } 4.144 + 4.145 + void doAction(EagerInterfaceCompletionTest test) { 4.146 + test.removeClass(classFile); 4.147 + }; 4.148 + } 4.149 + 4.150 + enum TestKind { 4.151 + ACCESS_ONLY("class C { B b; }"), 4.152 + SUPER("class C extends B {}"), 4.153 + METHOD("class C { void test(B b) { b.m(); } }"), 4.154 + FIELD("class C { void test(B b) { boolean b2 = b.f; } }"), 4.155 + CONSTR("class C { void test() { new B(); } }"); 4.156 + 4.157 + JavaSource source; 4.158 + 4.159 + private TestKind(final String code) { 4.160 + this.source = new JavaSource("Test2.java", code); 4.161 + } 4.162 + 4.163 + boolean completionFailure(ActionKind ak, HierarchyKind hk) { 4.164 + switch (this) { 4.165 + case ACCESS_ONLY: 4.166 + case CONSTR: return ak == ActionKind.REMOVE_B; 4.167 + case FIELD: 4.168 + case SUPER: return true; 4.169 + case METHOD: return hk != HierarchyKind.INTERFACE || ak == ActionKind.REMOVE_B; 4.170 + default: throw new AssertionError("Unexpected test kind " + this); 4.171 + } 4.172 + } 4.173 + } 4.174 + 4.175 + public static void main(String[] args) throws Exception { 4.176 + String SCRATCH_DIR = System.getProperty("user.dir"); 4.177 + JavaCompiler javacTool = ToolProvider.getSystemJavaCompiler(); 4.178 + int n = 0; 4.179 + for (HierarchyKind hierarchyKind : HierarchyKind.values()) { 4.180 + for (TestKind testKind : TestKind.values()) { 4.181 + for (ActionKind actionKind : ActionKind.values()) { 4.182 + File testDir = new File(SCRATCH_DIR, "test"+n); 4.183 + new EagerInterfaceCompletionTest(javacTool, testDir, hierarchyKind, testKind, actionKind).test(); 4.184 + n++; 4.185 + } 4.186 + } 4.187 + } 4.188 + if (nerrors > 0) { 4.189 + throw new AssertionError("Some errors have been detected"); 4.190 + } 4.191 + } 4.192 + 4.193 + static class JavaSource extends SimpleJavaFileObject { 4.194 + 4.195 + String source; 4.196 + 4.197 + public JavaSource(String filename, String source) { 4.198 + super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE); 4.199 + this.source = source; 4.200 + } 4.201 + 4.202 + @Override 4.203 + public CharSequence getCharContent(boolean ignoreEncodingErrors) { 4.204 + return source; 4.205 + } 4.206 + } 4.207 + 4.208 + static int nerrors = 0; 4.209 +}