Tue, 07 Sep 2010 17:31:54 +0100
6337171: javac should create bridge methods when type variable bounds restricted
Summary: javac should add synthetic overrides for inherited abstract methods in order to preserve binary compatibility
Reviewed-by: jjg
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Flags.java Mon Sep 06 12:55:09 2010 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Flags.java Tue Sep 07 17:31:54 2010 +0100 1.3 @@ -241,6 +241,12 @@ 1.4 */ 1.5 public static final long POLYMORPHIC_SIGNATURE = 1L<<40; 1.6 1.7 + /** 1.8 + * Flag that marks a special kind of bridge methods (the ones that 1.9 + * come from restricted supertype bounds) 1.10 + */ 1.11 + public static final long OVERRIDE_BRIDGE = 1L<<41; 1.12 + 1.13 /** Modifier masks. 1.14 */ 1.15 public static final int
2.1 --- a/src/share/classes/com/sun/tools/javac/code/Scope.java Mon Sep 06 12:55:09 2010 -0700 2.2 +++ b/src/share/classes/com/sun/tools/javac/code/Scope.java Tue Sep 07 17:31:54 2010 +0100 2.3 @@ -272,6 +272,12 @@ 2.4 return false; 2.5 } 2.6 2.7 + static final Filter<Symbol> noFilter = new Filter<Symbol>() { 2.8 + public boolean accepts(Symbol s) { 2.9 + return true; 2.10 + } 2.11 + }; 2.12 + 2.13 /** Return the entry associated with given name, starting in 2.14 * this scope and proceeding outwards. If no entry was found, 2.15 * return the sentinel, which is characterized by having a null in 2.16 @@ -279,13 +285,20 @@ 2.17 * for regular entries. 2.18 */ 2.19 public Entry lookup(Name name) { 2.20 + return lookup(name, noFilter); 2.21 + } 2.22 + public Entry lookup(Name name, Filter<Symbol> sf) { 2.23 Entry e = table[name.hashCode() & hashMask]; 2.24 - while (e.scope != null && e.sym.name != name) 2.25 + while (e.scope != null && (e.sym.name != name || !sf.accepts(e.sym))) 2.26 e = e.shadowed; 2.27 return e; 2.28 } 2.29 2.30 public Iterable<Symbol> getElements() { 2.31 + return getElements(noFilter); 2.32 + } 2.33 + 2.34 + public Iterable<Symbol> getElements(final Filter<Symbol> sf) { 2.35 return new Iterable<Symbol>() { 2.36 public Iterator<Symbol> iterator() { 2.37 return new Iterator<Symbol>() { 2.38 @@ -301,7 +314,9 @@ 2.39 2.40 public Symbol next() { 2.41 Symbol sym = (currEntry == null ? null : currEntry.sym); 2.42 - currEntry = currEntry.sibling; 2.43 + if (currEntry != null) { 2.44 + currEntry = currEntry.sibling; 2.45 + } 2.46 update(); 2.47 return sym; 2.48 } 2.49 @@ -311,9 +326,17 @@ 2.50 } 2.51 2.52 private void update() { 2.53 + skipToNextMatchingEntry(); 2.54 while (currEntry == null && currScope.next != null) { 2.55 currScope = currScope.next; 2.56 currEntry = currScope.elems; 2.57 + skipToNextMatchingEntry(); 2.58 + } 2.59 + } 2.60 + 2.61 + void skipToNextMatchingEntry() { 2.62 + while (currEntry != null && !sf.accepts(currEntry.sym)) { 2.63 + currEntry = currEntry.sibling; 2.64 } 2.65 } 2.66 };
3.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Mon Sep 06 12:55:09 2010 -0700 3.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Tue Sep 07 17:31:54 2010 +0100 3.3 @@ -1217,7 +1217,18 @@ 3.4 * as possible implementations. 3.5 */ 3.6 public MethodSymbol implementation(TypeSymbol origin, Types types, boolean checkResult) { 3.7 - MethodSymbol res = types.implementation(this, origin, types, checkResult); 3.8 + return implementation(origin, types, checkResult, implementation_filter); 3.9 + } 3.10 + // where 3.11 + private static final Filter<Symbol> implementation_filter = new Filter<Symbol>() { 3.12 + public boolean accepts(Symbol s) { 3.13 + return s.kind == Kinds.MTH && 3.14 + (s.flags() & SYNTHETIC) == 0; 3.15 + } 3.16 + }; 3.17 + 3.18 + public MethodSymbol implementation(TypeSymbol origin, Types types, boolean checkResult, Filter<Symbol> implFilter) { 3.19 + MethodSymbol res = types.implementation(this, origin, types, checkResult, implFilter); 3.20 if (res != null) 3.21 return res; 3.22 // if origin is derived from a raw type, we might have missed
4.1 --- a/src/share/classes/com/sun/tools/javac/code/Types.java Mon Sep 06 12:55:09 2010 -0700 4.2 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Tue Sep 07 17:31:54 2010 +0100 4.3 @@ -1974,45 +1974,74 @@ 4.4 hasSameArgs(t, erasure(s)) || hasSameArgs(erasure(t), s); 4.5 } 4.6 4.7 - private WeakHashMap<MethodSymbol, SoftReference<Map<TypeSymbol, MethodSymbol>>> implCache_check = 4.8 - new WeakHashMap<MethodSymbol, SoftReference<Map<TypeSymbol, MethodSymbol>>>(); 4.9 - 4.10 - private WeakHashMap<MethodSymbol, SoftReference<Map<TypeSymbol, MethodSymbol>>> implCache_nocheck = 4.11 - new WeakHashMap<MethodSymbol, SoftReference<Map<TypeSymbol, MethodSymbol>>>(); 4.12 - 4.13 - public MethodSymbol implementation(MethodSymbol ms, TypeSymbol origin, Types types, boolean checkResult) { 4.14 - Map<MethodSymbol, SoftReference<Map<TypeSymbol, MethodSymbol>>> implCache = checkResult ? 4.15 - implCache_check : implCache_nocheck; 4.16 - SoftReference<Map<TypeSymbol, MethodSymbol>> ref_cache = implCache.get(ms); 4.17 - Map<TypeSymbol, MethodSymbol> cache = ref_cache != null ? ref_cache.get() : null; 4.18 - if (cache == null) { 4.19 - cache = new HashMap<TypeSymbol, MethodSymbol>(); 4.20 - implCache.put(ms, new SoftReference<Map<TypeSymbol, MethodSymbol>>(cache)); 4.21 + // <editor-fold defaultstate="collapsed" desc="Determining method implementation in given site"> 4.22 + class ImplementationCache { 4.23 + 4.24 + private WeakHashMap<MethodSymbol, SoftReference<Map<TypeSymbol, Entry>>> _map = 4.25 + new WeakHashMap<MethodSymbol, SoftReference<Map<TypeSymbol, Entry>>>(); 4.26 + 4.27 + class Entry { 4.28 + final MethodSymbol cachedImpl; 4.29 + final Filter<Symbol> implFilter; 4.30 + final boolean checkResult; 4.31 + 4.32 + public Entry(MethodSymbol cachedImpl, 4.33 + Filter<Symbol> scopeFilter, 4.34 + boolean checkResult) { 4.35 + this.cachedImpl = cachedImpl; 4.36 + this.implFilter = scopeFilter; 4.37 + this.checkResult = checkResult; 4.38 + } 4.39 + 4.40 + boolean matches(Filter<Symbol> scopeFilter, boolean checkResult) { 4.41 + return this.implFilter == scopeFilter && 4.42 + this.checkResult == checkResult; 4.43 + } 4.44 } 4.45 - MethodSymbol impl = cache.get(origin); 4.46 - if (impl == null) { 4.47 + 4.48 + MethodSymbol get(MethodSymbol ms, TypeSymbol origin, boolean checkResult, Filter<Symbol> implFilter) { 4.49 + SoftReference<Map<TypeSymbol, Entry>> ref_cache = _map.get(ms); 4.50 + Map<TypeSymbol, Entry> cache = ref_cache != null ? ref_cache.get() : null; 4.51 + if (cache == null) { 4.52 + cache = new HashMap<TypeSymbol, Entry>(); 4.53 + _map.put(ms, new SoftReference<Map<TypeSymbol, Entry>>(cache)); 4.54 + } 4.55 + Entry e = cache.get(origin); 4.56 + if (e == null || 4.57 + !e.matches(implFilter, checkResult)) { 4.58 + MethodSymbol impl = implementationInternal(ms, origin, Types.this, checkResult, implFilter); 4.59 + cache.put(origin, new Entry(impl, implFilter, checkResult)); 4.60 + return impl; 4.61 + } 4.62 + else { 4.63 + return e.cachedImpl; 4.64 + } 4.65 + } 4.66 + 4.67 + private MethodSymbol implementationInternal(MethodSymbol ms, TypeSymbol origin, Types types, boolean checkResult, Filter<Symbol> implFilter) { 4.68 for (Type t = origin.type; t.tag == CLASS || t.tag == TYPEVAR; t = types.supertype(t)) { 4.69 while (t.tag == TYPEVAR) 4.70 t = t.getUpperBound(); 4.71 TypeSymbol c = t.tsym; 4.72 - for (Scope.Entry e = c.members().lookup(ms.name); 4.73 + for (Scope.Entry e = c.members().lookup(ms.name, implFilter); 4.74 e.scope != null; 4.75 e = e.next()) { 4.76 - if (e.sym.kind == Kinds.MTH) { 4.77 - MethodSymbol m = (MethodSymbol) e.sym; 4.78 - if (m.overrides(ms, origin, types, checkResult) && 4.79 - (m.flags() & SYNTHETIC) == 0) { 4.80 - impl = m; 4.81 - cache.put(origin, m); 4.82 - return impl; 4.83 - } 4.84 - } 4.85 + if (e.sym != null && 4.86 + e.sym.overrides(ms, origin, types, checkResult)) 4.87 + return (MethodSymbol)e.sym; 4.88 } 4.89 } 4.90 + return null; 4.91 } 4.92 - return impl; 4.93 } 4.94 4.95 + private ImplementationCache implCache = new ImplementationCache(); 4.96 + 4.97 + public MethodSymbol implementation(MethodSymbol ms, TypeSymbol origin, Types types, boolean checkResult, Filter<Symbol> implFilter) { 4.98 + return implCache.get(ms, origin, checkResult, implFilter); 4.99 + } 4.100 + // </editor-fold> 4.101 + 4.102 /** 4.103 * Does t have the same arguments as s? It is assumed that both 4.104 * types are (possibly polymorphic) method types. Monomorphic
5.1 --- a/src/share/classes/com/sun/tools/javac/comp/TransTypes.java Mon Sep 06 12:55:09 2010 -0700 5.2 +++ b/src/share/classes/com/sun/tools/javac/comp/TransTypes.java Tue Sep 07 17:31:54 2010 +0100 5.3 @@ -283,12 +283,13 @@ 5.4 ListBuffer<JCTree> bridges) { 5.5 if (sym.kind == MTH && 5.6 sym.name != names.init && 5.7 - (sym.flags() & (PRIVATE | SYNTHETIC | STATIC)) == 0 && 5.8 + (sym.flags() & (PRIVATE | STATIC)) == 0 && 5.9 + (sym.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC && 5.10 sym.isMemberOf(origin, types)) 5.11 { 5.12 MethodSymbol meth = (MethodSymbol)sym; 5.13 MethodSymbol bridge = meth.binaryImplementation(origin, types); 5.14 - MethodSymbol impl = meth.implementation(origin, types, true); 5.15 + MethodSymbol impl = meth.implementation(origin, types, true, overrideBridgeFilter); 5.16 if (bridge == null || 5.17 bridge == meth || 5.18 (impl != null && !bridge.owner.isSubClass(impl.owner, types))) { 5.19 @@ -304,7 +305,7 @@ 5.20 // reflection design error. 5.21 addBridge(pos, meth, impl, origin, false, bridges); 5.22 } 5.23 - } else if ((bridge.flags() & SYNTHETIC) != 0) { 5.24 + } else if ((bridge.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) == SYNTHETIC) { 5.25 MethodSymbol other = overridden.get(bridge); 5.26 if (other != null && other != meth) { 5.27 if (impl == null || !impl.overrides(other, origin, types, true)) { 5.28 @@ -327,6 +328,11 @@ 5.29 } 5.30 } 5.31 // where 5.32 + Filter<Symbol> overrideBridgeFilter = new Filter<Symbol>() { 5.33 + public boolean accepts(Symbol s) { 5.34 + return (s.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC; 5.35 + } 5.36 + }; 5.37 /** 5.38 * @param method The symbol for which a bridge might have to be added 5.39 * @param impl The implementation of method 5.40 @@ -754,6 +760,90 @@ 5.41 return types.erasure(t); 5.42 } 5.43 5.44 + private boolean boundsRestricted(ClassSymbol c) { 5.45 + Type st = types.supertype(c.type); 5.46 + if (st.isParameterized()) { 5.47 + List<Type> actuals = st.allparams(); 5.48 + List<Type> formals = st.tsym.type.allparams(); 5.49 + while (!actuals.isEmpty() && !formals.isEmpty()) { 5.50 + Type actual = actuals.head; 5.51 + Type formal = formals.head; 5.52 + 5.53 + if (!types.isSameType(types.erasure(actual), 5.54 + types.erasure(formal))) 5.55 + return true; 5.56 + 5.57 + actuals = actuals.tail; 5.58 + formals = formals.tail; 5.59 + } 5.60 + } 5.61 + return false; 5.62 + } 5.63 + 5.64 + private List<JCTree> addOverrideBridgesIfNeeded(DiagnosticPosition pos, 5.65 + final ClassSymbol c) { 5.66 + ListBuffer<JCTree> buf = ListBuffer.lb(); 5.67 + if (c.isInterface() || !boundsRestricted(c)) 5.68 + return buf.toList(); 5.69 + Type t = types.supertype(c.type); 5.70 + Scope s = t.tsym.members(); 5.71 + if (s.elems != null) { 5.72 + for (Symbol sym : s.getElements(new NeedsOverridBridgeFilter(c))) { 5.73 + 5.74 + MethodSymbol m = (MethodSymbol)sym; 5.75 + MethodSymbol member = (MethodSymbol)m.asMemberOf(c.type, types); 5.76 + MethodSymbol impl = m.implementation(c, types, false); 5.77 + 5.78 + if ((impl == null || impl.owner != c) && 5.79 + !types.isSameType(member.erasure(types), m.erasure(types))) { 5.80 + addOverrideBridges(pos, m, member, c, buf); 5.81 + } 5.82 + } 5.83 + } 5.84 + return buf.toList(); 5.85 + } 5.86 + // where 5.87 + class NeedsOverridBridgeFilter implements Filter<Symbol> { 5.88 + 5.89 + ClassSymbol c; 5.90 + 5.91 + NeedsOverridBridgeFilter(ClassSymbol c) { 5.92 + this.c = c; 5.93 + } 5.94 + public boolean accepts(Symbol s) { 5.95 + return s.kind == MTH && 5.96 + !s.isConstructor() && 5.97 + s.isInheritedIn(c, types) && 5.98 + (s.flags() & FINAL) == 0 && 5.99 + (s.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC; 5.100 + } 5.101 + } 5.102 + 5.103 + private void addOverrideBridges(DiagnosticPosition pos, 5.104 + MethodSymbol impl, 5.105 + MethodSymbol member, 5.106 + ClassSymbol c, 5.107 + ListBuffer<JCTree> bridges) { 5.108 + Type implErasure = impl.erasure(types); 5.109 + long flags = (impl.flags() & AccessFlags) | SYNTHETIC | BRIDGE | OVERRIDE_BRIDGE; 5.110 + member = new MethodSymbol(flags, member.name, member.type, c); 5.111 + JCMethodDecl md = make.MethodDef(member, null); 5.112 + JCExpression receiver = make.Super(types.supertype(c.type).tsym.erasure(types), c); 5.113 + Type calltype = erasure(impl.type.getReturnType()); 5.114 + JCExpression call = 5.115 + make.Apply(null, 5.116 + make.Select(receiver, impl).setType(calltype), 5.117 + translateArgs(make.Idents(md.params), 5.118 + implErasure.getParameterTypes(), null)) 5.119 + .setType(calltype); 5.120 + JCStatement stat = (member.getReturnType().tag == VOID) 5.121 + ? make.Exec(call) 5.122 + : make.Return(coerce(call, member.erasure(types).getReturnType())); 5.123 + md.body = make.Block(0, List.of(stat)); 5.124 + c.members().enter(member); 5.125 + bridges.append(md); 5.126 + } 5.127 + 5.128 /************************************************************************** 5.129 * main method 5.130 *************************************************************************/ 5.131 @@ -786,6 +876,7 @@ 5.132 make.at(tree.pos); 5.133 if (addBridges) { 5.134 ListBuffer<JCTree> bridges = new ListBuffer<JCTree>(); 5.135 + bridges.appendList(addOverrideBridgesIfNeeded(tree, c)); 5.136 if ((tree.sym.flags() & INTERFACE) == 0) 5.137 addBridges(tree.pos(), tree.sym, bridges); 5.138 tree.defs = bridges.toList().prependList(tree.defs);
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/share/classes/com/sun/tools/javac/util/Filter.java Tue Sep 07 17:31:54 2010 +0100 6.3 @@ -0,0 +1,39 @@ 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. Oracle designates this 6.11 + * particular file as subject to the "Classpath" exception as provided 6.12 + * by Oracle in the LICENSE file that accompanied this code. 6.13 + * 6.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 6.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 6.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 6.17 + * version 2 for more details (a copy is included in the LICENSE file that 6.18 + * accompanied this code). 6.19 + * 6.20 + * You should have received a copy of the GNU General Public License version 6.21 + * 2 along with this work; if not, write to the Free Software Foundation, 6.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 6.23 + * 6.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 6.25 + * or visit www.oracle.com if you need additional information or have any 6.26 + * questions. 6.27 + */ 6.28 + 6.29 +package com.sun.tools.javac.util; 6.30 + 6.31 +/** 6.32 + * Simple filter acting as a boolean predicate. Method accepts return true if 6.33 + * the supplied element matches against the filter. 6.34 + */ 6.35 +public interface Filter<T> { 6.36 + /** 6.37 + * Does this element match against the filter? 6.38 + * @param t element to be checked 6.39 + * @return true if the element satisfy constraints imposed by filter 6.40 + */ 6.41 + boolean accepts(T t); 6.42 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/test/tools/javac/generics/OverrideBridge.java Tue Sep 07 17:31:54 2010 +0100 7.3 @@ -0,0 +1,163 @@ 7.4 +/* 7.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 7.7 + * 7.8 + * This code is free software; you can redistribute it and/or modify it 7.9 + * under the terms of the GNU General Public License version 2 only, as 7.10 + * published by the Free Software Foundation. 7.11 + * 7.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 7.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 7.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 7.15 + * version 2 for more details (a copy is included in the LICENSE file that 7.16 + * accompanied this code). 7.17 + * 7.18 + * You should have received a copy of the GNU General Public License version 7.19 + * 2 along with this work; if not, write to the Free Software Foundation, 7.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 7.21 + * 7.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 7.23 + * or visit www.oracle.com if you need additional information or have any 7.24 + * questions. 7.25 + */ 7.26 + 7.27 +/* 7.28 + * @test 7.29 + * @bug 6337171 7.30 + * @summary javac should create bridge methods when type variable bounds restricted 7.31 + * @run main OverrideBridge 7.32 + */ 7.33 + 7.34 +import java.io.*; 7.35 +import java.net.URI; 7.36 +import java.util.ArrayList; 7.37 +import java.util.Arrays; 7.38 +import java.util.List; 7.39 +import java.util.Map; 7.40 +import java.util.HashMap; 7.41 +import javax.tools.JavaCompiler; 7.42 +import javax.tools.JavaFileObject; 7.43 +import javax.tools.SimpleJavaFileObject; 7.44 +import javax.tools.ToolProvider; 7.45 + 7.46 +import com.sun.source.util.JavacTask; 7.47 +import com.sun.tools.classfile.ClassFile; 7.48 +import com.sun.tools.classfile.ConstantPoolException; 7.49 +import com.sun.tools.classfile.Descriptor.InvalidDescriptor; 7.50 +import com.sun.tools.classfile.Method; 7.51 + 7.52 +public class OverrideBridge { 7.53 + 7.54 + enum Implementation { 7.55 + IMPLICIT(""), 7.56 + EXPLICIT("@Override public abstract X m(X x);"); 7.57 + 7.58 + String impl; 7.59 + 7.60 + Implementation(String impl) { 7.61 + this.impl = impl; 7.62 + } 7.63 + } 7.64 + 7.65 + static class JavaSource extends SimpleJavaFileObject { 7.66 + 7.67 + final static String sourceStub = 7.68 + "abstract class A<X> {\n" + 7.69 + " public abstract X m(X x);\n" + 7.70 + "}\n" + 7.71 + "interface I<X> {\n" + 7.72 + "X m(X x);\n" + 7.73 + "}\n" + 7.74 + "abstract class B<X extends B<X>> extends A<X> implements I<X> { #B }\n" + 7.75 + "abstract class C<X extends C<X>> extends B<X> { #C }\n" + 7.76 + "abstract class D<X extends D<X>> extends C<X> { #D }\n"; 7.77 + 7.78 + String source; 7.79 + 7.80 + public JavaSource(Implementation implB, Implementation implC, Implementation implD) { 7.81 + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 7.82 + source = sourceStub.replace("#B", implB.impl).replace("#C", implC.impl).replace("#D", implD.impl); 7.83 + } 7.84 + 7.85 + @Override 7.86 + public CharSequence getCharContent(boolean ignoreEncodingErrors) { 7.87 + return source; 7.88 + } 7.89 + } 7.90 + 7.91 + public static void main(String... args) throws Exception { 7.92 + Map<ClassFile, List<Method>> refMembers = 7.93 + compile(Implementation.EXPLICIT, Implementation.EXPLICIT, Implementation.EXPLICIT, "ref"); 7.94 + int i = 0; 7.95 + for (Implementation implB : Implementation.values()) { 7.96 + for (Implementation implC : Implementation.values()) { 7.97 + for (Implementation implD : Implementation.values()) { 7.98 + Map<ClassFile, List<Method>> membersToCheck = compile(implB, implC, implD, "out_" + i++); 7.99 + check(refMembers, membersToCheck); 7.100 + } 7.101 + } 7.102 + } 7.103 + } 7.104 + 7.105 + static String workDir = System.getProperty("user.dir"); 7.106 + 7.107 + static Map<ClassFile, List<Method>> compile(Implementation implB, Implementation implC, Implementation implD, String destPath) throws Exception { 7.108 + File destDir = new File(workDir, destPath); destDir.mkdir(); 7.109 + final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 7.110 + JavaSource source = new JavaSource(implB, implC, implD); 7.111 + JavacTask ct = (JavacTask)tool.getTask(null, null, null, 7.112 + Arrays.asList("-d", destPath), null, Arrays.asList(source)); 7.113 + ct.generate(); 7.114 + Map<ClassFile, List<Method>> members = new HashMap<>(); 7.115 + addMembers(destDir, members); 7.116 + return members; 7.117 + } 7.118 + 7.119 + static void addMembers(File destDir, Map<ClassFile, List<Method>> members) { 7.120 + String[] names = { "B.class", "C.class", "D.class" }; 7.121 + try { 7.122 + for (String name : names) { 7.123 + File f = new File(destDir, name); 7.124 + ClassFile cf = ClassFile.read(f); 7.125 + members.put(cf, readMethod(cf, "m")); 7.126 + } 7.127 + } catch (Exception e) { 7.128 + e.printStackTrace(); 7.129 + throw new Error("error reading classes"); 7.130 + } 7.131 + } 7.132 + 7.133 + static List<Method> readMethod(ClassFile cf, String name) throws ConstantPoolException { 7.134 + List<Method> buf = new ArrayList<>(); 7.135 + for (Method m : cf.methods) { 7.136 + if (m.getName(cf.constant_pool).equals(name)) { 7.137 + buf.add(m); 7.138 + } 7.139 + } 7.140 + return buf; 7.141 + } 7.142 + 7.143 + static void check(Map<ClassFile, List<Method>> refMembers, Map<ClassFile, List<Method>> membersToCheck) throws ConstantPoolException, InvalidDescriptor { 7.144 + for (Map.Entry<ClassFile, List<Method>> ref : refMembers.entrySet()) { 7.145 + ClassFile cRef = ref.getKey(); 7.146 + for (Method mRef : ref.getValue()) { 7.147 + boolean ok = false; 7.148 + for (Map.Entry<ClassFile, List<Method>> toCheck : membersToCheck.entrySet()) { 7.149 + ClassFile cToCheck = toCheck.getKey(); 7.150 + for (Method mToCheck : toCheck.getValue()) { 7.151 + if (cRef.getName().equals(cToCheck.getName()) && 7.152 + mRef.descriptor.getReturnType(cRef.constant_pool).equals( 7.153 + mToCheck.descriptor.getReturnType(cToCheck.constant_pool)) && 7.154 + mRef.descriptor.getParameterTypes(cRef.constant_pool).equals( 7.155 + mToCheck.descriptor.getParameterTypes(cToCheck.constant_pool))) { 7.156 + ok = true; 7.157 + } 7.158 + } 7.159 + } 7.160 + if (!ok) { 7.161 + throw new AssertionError("Matching method descriptor for " + mRef.descriptor.getParameterTypes(cRef.constant_pool) + "not found"); 7.162 + } 7.163 + } 7.164 + } 7.165 + } 7.166 +}