6337171: javac should create bridge methods when type variable bounds restricted

Tue, 07 Sep 2010 17:31:54 +0100

author
mcimadamore
date
Tue, 07 Sep 2010 17:31:54 +0100
changeset 673
7ae4016c5938
parent 672
ea54372637a5
child 674
584365f256a7

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

src/share/classes/com/sun/tools/javac/code/Flags.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/code/Scope.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/code/Symbol.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/code/Types.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/TransTypes.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/util/Filter.java file | annotate | diff | comparison | revisions
test/tools/javac/generics/OverrideBridge.java file | annotate | diff | comparison | revisions
     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 +}

mercurial