# HG changeset patch # User lana # Date 1374082853 25200 # Node ID e990e6bcecbe8a7f282e3a1de79499f609211c83 # Parent 10711bd8bb2d6c3a9fa90da53299c1620ac96a1f# Parent 39ec5d8a691b46a5bec6847b19bd87ef71051a74 Merge diff -r 10711bd8bb2d -r e990e6bcecbe .hgtags --- a/.hgtags Wed Jul 17 15:08:58 2013 +0200 +++ b/.hgtags Wed Jul 17 10:40:53 2013 -0700 @@ -219,3 +219,4 @@ 4cb1136231275a1f8af53f5bfdef0b488e4b5bab jdk8-b95 988aef3a8c3adac482363293f65e77ec4c5ce98d jdk8-b96 6a11a81a8824c17f6cd2ec8f8492e1229b694e96 jdk8-b97 +ce5a90df517bdceb2739d7dd3e6764b070def802 jdk8-b98 diff -r 10711bd8bb2d -r e990e6bcecbe src/share/classes/com/sun/tools/javac/code/Types.java --- a/src/share/classes/com/sun/tools/javac/code/Types.java Wed Jul 17 15:08:58 2013 +0200 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Wed Jul 17 10:40:53 2013 -0700 @@ -33,10 +33,15 @@ import java.util.Set; import java.util.WeakHashMap; +import javax.tools.JavaFileObject; + import com.sun.tools.javac.code.Attribute.RetentionPolicy; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Type.UndetVar.InferenceBound; +import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Check; +import com.sun.tools.javac.comp.Enter; +import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.jvm.ClassReader; import com.sun.tools.javac.util.*; import static com.sun.tools.javac.code.BoundKind.*; @@ -83,6 +88,7 @@ final boolean allowDefaultMethods; final ClassReader reader; final Check chk; + final Enter enter; JCDiagnostic.Factory diags; List warnStack = List.nil(); final Name capturedName; @@ -109,6 +115,7 @@ allowDefaultMethods = source.allowDefaultMethods(); reader = ClassReader.instance(context); chk = Check.instance(context); + enter = Enter.instance(context); capturedName = names.fromString(""); messages = JavacMessages.instance(context); diags = JCDiagnostic.Factory.instance(context); @@ -605,6 +612,84 @@ return site; } } + + /** + * Create a symbol for a class that implements a given functional interface + * and overrides its functional descriptor. This routine is used for two + * main purposes: (i) checking well-formedness of a functional interface; + * (ii) perform functional interface bridge calculation. + */ + public ClassSymbol makeFunctionalInterfaceClass(Env env, Name name, List targets, long cflags) { + Assert.check(targets.nonEmpty() && isFunctionalInterface(targets.head)); + Symbol descSym = findDescriptorSymbol(targets.head.tsym); + Type descType = findDescriptorType(targets.head); + ClassSymbol csym = new ClassSymbol(cflags, name, env.enclClass.sym.outermostClass()); + csym.completer = null; + csym.members_field = new Scope(csym); + MethodSymbol instDescSym = new MethodSymbol(descSym.flags(), descSym.name, descType, csym); + csym.members_field.enter(instDescSym); + Type.ClassType ctype = new Type.ClassType(Type.noType, List.nil(), csym); + ctype.supertype_field = syms.objectType; + ctype.interfaces_field = targets; + csym.type = ctype; + csym.sourcefile = ((ClassSymbol)csym.owner).sourcefile; + return csym; + } + + /** + * Find the minimal set of methods that are overridden by the functional + * descriptor in 'origin'. All returned methods are assumed to have different + * erased signatures. + */ + public List functionalInterfaceBridges(TypeSymbol origin) { + Assert.check(isFunctionalInterface(origin)); + Symbol descSym = findDescriptorSymbol(origin); + CompoundScope members = membersClosure(origin.type, false); + ListBuffer overridden = ListBuffer.lb(); + outer: for (Symbol m2 : members.getElementsByName(descSym.name, bridgeFilter)) { + if (m2 == descSym) continue; + else if (descSym.overrides(m2, origin, Types.this, false)) { + for (Symbol m3 : overridden) { + if (isSameType(m3.erasure(Types.this), m2.erasure(Types.this)) || + (m3.overrides(m2, origin, Types.this, false) && + (pendingBridges((ClassSymbol)origin, m3.enclClass()) || + (((MethodSymbol)m2).binaryImplementation((ClassSymbol)m3.owner, Types.this) != null)))) { + continue outer; + } + } + overridden.add(m2); + } + } + return overridden.toList(); + } + //where + private Filter bridgeFilter = new Filter() { + public boolean accepts(Symbol t) { + return t.kind == Kinds.MTH && + t.name != names.init && + t.name != names.clinit && + (t.flags() & SYNTHETIC) == 0; + } + }; + private boolean pendingBridges(ClassSymbol origin, TypeSymbol s) { + //a symbol will be completed from a classfile if (a) symbol has + //an associated file object with CLASS kind and (b) the symbol has + //not been entered + if (origin.classfile != null && + origin.classfile.getKind() == JavaFileObject.Kind.CLASS && + enter.getEnv(origin) == null) { + return false; + } + if (origin == s) { + return true; + } + for (Type t : interfaces(origin.type)) { + if (pendingBridges((ClassSymbol)t.tsym, s)) { + return true; + } + } + return false; + } // /** @@ -2672,6 +2757,7 @@ public boolean accepts(Symbol s) { return s.kind == Kinds.MTH && s.name == msym.name && + (s.flags() & SYNTHETIC) == 0 && s.isInheritedIn(site.tsym, Types.this) && overrideEquivalent(memberType(site, s), memberType(site, msym)); } diff -r 10711bd8bb2d -r e990e6bcecbe src/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Wed Jul 17 15:08:58 2013 +0200 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Wed Jul 17 10:40:53 2013 -0700 @@ -2330,13 +2330,12 @@ if (pt() != Type.recoveryType) { target = targetChecker.visit(target, that); lambdaType = types.findDescriptorType(target); - chk.checkFunctionalInterface(that, target); } else { target = Type.recoveryType; lambdaType = fallbackDescriptorType(that); } - setFunctionalInfo(that, pt(), lambdaType, target, resultInfo.checkContext.inferenceContext()); + setFunctionalInfo(localEnv, that, pt(), lambdaType, target, resultInfo.checkContext); if (lambdaType.hasTag(FORALL)) { //lambda expression target desc cannot be a generic method @@ -2715,13 +2714,12 @@ if (pt() != Type.recoveryType) { target = targetChecker.visit(pt(), that); desc = types.findDescriptorType(target); - chk.checkFunctionalInterface(that, target); } else { target = Type.recoveryType; desc = fallbackDescriptorType(that); } - setFunctionalInfo(that, pt(), desc, target, resultInfo.checkContext.inferenceContext()); + setFunctionalInfo(localEnv, that, pt(), desc, target, resultInfo.checkContext); List argtypes = desc.getParameterTypes(); Resolve.MethodCheck referenceCheck = rs.resolveMethodCheck; @@ -2941,31 +2939,37 @@ * might contain inference variables, we might need to register an hook in the * current inference context. */ - private void setFunctionalInfo(final JCFunctionalExpression fExpr, final Type pt, - final Type descriptorType, final Type primaryTarget, InferenceContext inferenceContext) { - if (inferenceContext.free(descriptorType)) { - inferenceContext.addFreeTypeListener(List.of(pt, descriptorType), new FreeTypeListener() { + private void setFunctionalInfo(final Env env, final JCFunctionalExpression fExpr, + final Type pt, final Type descriptorType, final Type primaryTarget, final CheckContext checkContext) { + if (checkContext.inferenceContext().free(descriptorType)) { + checkContext.inferenceContext().addFreeTypeListener(List.of(pt, descriptorType), new FreeTypeListener() { public void typesInferred(InferenceContext inferenceContext) { - setFunctionalInfo(fExpr, pt, inferenceContext.asInstType(descriptorType), - inferenceContext.asInstType(primaryTarget), inferenceContext); + setFunctionalInfo(env, fExpr, pt, inferenceContext.asInstType(descriptorType), + inferenceContext.asInstType(primaryTarget), checkContext); } }); } else { - ListBuffer targets = ListBuffer.lb(); + ListBuffer targets = ListBuffer.lb(); if (pt.hasTag(CLASS)) { if (pt.isCompound()) { - targets.append(primaryTarget.tsym); //this goes first + targets.append(types.removeWildcards(primaryTarget)); //this goes first for (Type t : ((IntersectionClassType)pt()).interfaces_field) { if (t != primaryTarget) { - targets.append(t.tsym); + targets.append(types.removeWildcards(t)); } } } else { - targets.append(pt.tsym); + targets.append(types.removeWildcards(primaryTarget)); } } fExpr.targets = targets.toList(); - fExpr.descriptorType = descriptorType; + if (checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.CHECK && + pt != Type.recoveryType) { + //check that functional interface class is well-formed + ClassSymbol csym = types.makeFunctionalInterfaceClass(env, + names.empty, List.of(fExpr.targets.head), ABSTRACT); + chk.checkImplementations(env.tree, csym, csym); + } } } @@ -4644,9 +4648,6 @@ @Override public void visitLambda(JCLambda that) { super.visitLambda(that); - if (that.descriptorType == null) { - that.descriptorType = syms.unknownType; - } if (that.targets == null) { that.targets = List.nil(); } @@ -4658,9 +4659,6 @@ if (that.sym == null) { that.sym = new MethodSymbol(0, names.empty, syms.unknownType, syms.noSymbol); } - if (that.descriptorType == null) { - that.descriptorType = syms.unknownType; - } if (that.targets == null) { that.targets = List.nil(); } diff -r 10711bd8bb2d -r e990e6bcecbe src/share/classes/com/sun/tools/javac/comp/Check.java --- a/src/share/classes/com/sun/tools/javac/comp/Check.java Wed Jul 17 15:08:58 2013 +0200 +++ b/src/share/classes/com/sun/tools/javac/comp/Check.java Wed Jul 17 10:40:53 2013 -0700 @@ -2276,24 +2276,6 @@ c.flags_field |= ACYCLIC; } - /** - * Check that functional interface methods would make sense when seen - * from the perspective of the implementing class - */ - void checkFunctionalInterface(JCTree tree, Type funcInterface) { - ClassType c = new ClassType(Type.noType, List.nil(), null); - ClassSymbol csym = new ClassSymbol(0, names.empty, c, syms.noSymbol); - c.interfaces_field = List.of(types.removeWildcards(funcInterface)); - c.supertype_field = syms.objectType; - c.tsym = csym; - csym.members_field = new Scope(csym); - Symbol descSym = types.findDescriptorSymbol(funcInterface.tsym); - Type descType = types.findDescriptorType(funcInterface); - csym.members_field.enter(new MethodSymbol(PUBLIC, descSym.name, descType, csym)); - csym.completer = null; - checkImplementations(tree, csym, csym); - } - /** Check that all methods which implement some * method conform to the method they implement. * @param tree The class definition whose members are checked. diff -r 10711bd8bb2d -r e990e6bcecbe src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Wed Jul 17 15:08:58 2013 +0200 +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Wed Jul 17 10:40:53 2013 -0700 @@ -100,6 +100,9 @@ /** Flag for alternate metafactories indicating the lambda object has multiple targets */ public static final int FLAG_MARKERS = 1 << 1; + /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */ + public static final int FLAG_BRIDGES = 1 << 2; + private class KlassInfo { /** @@ -321,7 +324,7 @@ int refKind = referenceKind(sym); //convert to an invokedynamic call - result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args); + result = makeMetafactoryIndyCall(context, refKind, sym, indy_args); } private JCIdent makeThis(Type type, Symbol owner) { @@ -382,7 +385,7 @@ //build a sam instance using an indy call to the meta-factory - result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args); + result = makeMetafactoryIndyCall(localContext, localContext.referenceKind(), refSym, indy_args); } /** @@ -606,8 +609,8 @@ make.Return(makeIndyCall( pos, syms.lambdaMetafactory, - names.altMetaFactory, - staticArgs, indyType, serArgs.toList())), + names.altMetafactory, + staticArgs, indyType, serArgs.toList(), samSym.name)), null); ListBuffer stmts = kInfo.deserializeCases.get(implMethodName); if (stmts == null) { @@ -905,22 +908,26 @@ kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge()); } + private MethodType typeToMethodType(Type mt) { + Type type = types.erasure(mt); + return new MethodType(type.getParameterTypes(), + type.getReturnType(), + type.getThrownTypes(), + syms.methodClass); + } + /** * Generate an indy method call to the meta factory */ - private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory, - boolean isSerializable, int refKind, Symbol refSym, List indy_args) { + private JCExpression makeMetafactoryIndyCall(TranslationContext context, + int refKind, Symbol refSym, List indy_args) { + JCFunctionalExpression tree = context.tree; //determine the static bsm args - Type mtype = types.erasure(tree.descriptorType); MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym); List staticArgs = List.of( - new Pool.MethodHandle(ClassFile.REF_invokeInterface, - types.findDescriptorSymbol(tree.type.tsym), types), + typeToMethodType(samSym.type), new Pool.MethodHandle(refKind, refSym, types), - new MethodType(mtype.getParameterTypes(), - mtype.getReturnType(), - mtype.getThrownTypes(), - syms.methodClass)); + typeToMethodType(tree.getDescriptorType(types))); //computed indy arg types ListBuffer indy_args_types = ListBuffer.lb(); @@ -934,31 +941,46 @@ List.nil(), syms.methodClass); - Name metafactoryName = needsAltMetafactory ? - names.altMetaFactory : names.metaFactory; + Name metafactoryName = context.needsAltMetafactory() ? + names.altMetafactory : names.metafactory; - if (needsAltMetafactory) { + if (context.needsAltMetafactory()) { ListBuffer markers = ListBuffer.lb(); - for (Symbol t : tree.targets.tail) { - if (t != syms.serializableType.tsym) { - markers.append(t); + for (Type t : tree.targets.tail) { + if (t.tsym != syms.serializableType.tsym) { + markers.append(t.tsym); } } - int flags = isSerializable? FLAG_SERIALIZABLE : 0; + int flags = context.isSerializable() ? FLAG_SERIALIZABLE : 0; boolean hasMarkers = markers.nonEmpty(); - flags |= hasMarkers ? FLAG_MARKERS : 0; + boolean hasBridges = context.bridges.nonEmpty(); + if (hasMarkers) { + flags |= FLAG_MARKERS; + } + if (hasBridges) { + flags |= FLAG_BRIDGES; + } staticArgs = staticArgs.append(flags); if (hasMarkers) { staticArgs = staticArgs.append(markers.length()); staticArgs = staticArgs.appendList(markers.toList()); } - if (isSerializable) { + if (hasBridges) { + staticArgs = staticArgs.append(context.bridges.length() - 1); + for (Symbol s : context.bridges) { + Type s_erasure = s.erasure(types); + if (!types.isSameType(s_erasure, samSym.erasure(types))) { + staticArgs = staticArgs.append(s.erasure(types)); + } + } + } + if (context.isSerializable()) { addDeserializationCase(refKind, refSym, tree.type, samSym, tree, staticArgs, indyType); } } - return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args); + return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name); } /** @@ -966,7 +988,8 @@ * arguments types */ private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, - List staticArgs, MethodType indyType, List indyArgs) { + List staticArgs, MethodType indyType, List indyArgs, + Name methName) { int prevPos = make.pos; try { make.at(pos); @@ -978,7 +1001,7 @@ bsmName, bsm_staticArgs, List.nil()); DynamicMethodSymbol dynSym = - new DynamicMethodSymbol(names.lambda, + new DynamicMethodSymbol(methName, syms.noSymbol, bsm.isStatic() ? ClassFile.REF_invokeStatic : @@ -1299,7 +1322,6 @@ // Make lambda holding the new-class call JCLambda slam = make.Lambda(params, nc); - slam.descriptorType = tree.descriptorType; slam.targets = tree.targets; slam.type = tree.type; slam.pos = tree.pos; @@ -1634,23 +1656,30 @@ /** the enclosing translation context (set for nested lambdas/mref) */ TranslationContext prev; + /** list of methods to be bridged by the meta-factory */ + List bridges; + TranslationContext(T tree) { this.tree = tree; this.owner = owner(); this.depth = frameStack.size() - 1; this.prev = context(); + ClassSymbol csym = + types.makeFunctionalInterfaceClass(attrEnv, names.empty, tree.targets, ABSTRACT | INTERFACE); + this.bridges = types.functionalInterfaceBridges(csym); } /** does this functional expression need to be created using alternate metafactory? */ boolean needsAltMetafactory() { - return (tree.targets.length() > 1 || - isSerializable()); + return tree.targets.length() > 1 || + isSerializable() || + bridges.length() > 1; } /** does this functional expression require serialization support? */ boolean isSerializable() { - for (Symbol target : tree.targets) { - if (types.asSuper(target.type, syms.serializableType.tsym) != null) { + for (Type target : tree.targets) { + if (types.asSuper(target, syms.serializableType.tsym) != null) { return true; } } @@ -1833,7 +1862,7 @@ } Type generatedLambdaSig() { - return types.erasure(tree.descriptorType); + return types.erasure(tree.getDescriptorType(types)); } } @@ -1909,7 +1938,7 @@ } Type bridgedRefSig() { - return types.erasure(types.findDescriptorSymbol(tree.targets.head).type); + return types.erasure(types.findDescriptorSymbol(tree.targets.head.tsym).type); } } } diff -r 10711bd8bb2d -r e990e6bcecbe src/share/classes/com/sun/tools/javac/comp/TransTypes.java --- a/src/share/classes/com/sun/tools/javac/comp/TransTypes.java Wed Jul 17 15:08:58 2013 +0200 +++ b/src/share/classes/com/sun/tools/javac/comp/TransTypes.java Wed Jul 17 10:40:53 2013 -0700 @@ -68,6 +68,7 @@ private TreeMaker make; private Enter enter; private boolean allowEnums; + private boolean allowInterfaceBridges; private Types types; private final Resolve resolve; @@ -91,6 +92,7 @@ Source source = Source.instance(context); allowEnums = source.allowEnums(); addBridges = source.addBridges(); + allowInterfaceBridges = source.allowDefaultMethods(); types = Types.instance(context); make = TreeMaker.instance(context); resolve = Resolve.instance(context); @@ -252,7 +254,8 @@ // Create a bridge method symbol and a bridge definition without a body. Type bridgeType = meth.erasure(types); - long flags = impl.flags() & AccessFlags | SYNTHETIC | BRIDGE; + long flags = impl.flags() & AccessFlags | SYNTHETIC | BRIDGE | + (origin.isInterface() ? DEFAULT : 0); if (hypothetical) flags |= HYPOTHETICAL; MethodSymbol bridge = new MethodSymbol(flags, meth.name, @@ -387,11 +390,12 @@ } } // where - Filter overrideBridgeFilter = new Filter() { + private Filter overrideBridgeFilter = new Filter() { public boolean accepts(Symbol s) { return (s.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC; } }; + /** * @param method The symbol for which a bridge might have to be added * @param impl The implementation of method @@ -999,8 +1003,9 @@ ListBuffer bridges = new ListBuffer(); if (false) //see CR: 6996415 bridges.appendList(addOverrideBridgesIfNeeded(tree, c)); - if ((tree.sym.flags() & INTERFACE) == 0) - addBridges(tree.pos(), tree.sym, bridges); + if (allowInterfaceBridges || (tree.sym.flags() & INTERFACE) == 0) { + addBridges(tree.pos(), c, bridges); + } tree.defs = bridges.toList().prependList(tree.defs); } tree.type = erasure(tree.type); diff -r 10711bd8bb2d -r e990e6bcecbe src/share/classes/com/sun/tools/javac/tree/JCTree.java --- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java Wed Jul 17 15:08:58 2013 +0200 +++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java Wed Jul 17 10:40:53 2013 -0700 @@ -641,10 +641,12 @@ polyKind = PolyKind.POLY; } - /** target descriptor inferred for this functional expression. */ - public Type descriptorType; /** list of target types inferred for this functional expression. */ - public List targets; + public List targets; + + public Type getDescriptorType(Types types) { + return types.findDescriptorType(targets.head); + } } /** diff -r 10711bd8bb2d -r e990e6bcecbe src/share/classes/com/sun/tools/javac/util/Names.java --- a/src/share/classes/com/sun/tools/javac/util/Names.java Wed Jul 17 15:08:58 2013 +0200 +++ b/src/share/classes/com/sun/tools/javac/util/Names.java Wed Jul 17 10:40:53 2013 -0700 @@ -174,8 +174,8 @@ //lambda-related public final Name lambda; - public final Name metaFactory; - public final Name altMetaFactory; + public final Name metafactory; + public final Name altMetafactory; public final Name.Table table; @@ -310,8 +310,8 @@ //lambda-related lambda = fromString("lambda$"); - metaFactory = fromString("metaFactory"); - altMetaFactory = fromString("altMetaFactory"); + metafactory = fromString("metafactory"); + altMetafactory = fromString("altMetafactory"); } protected Name.Table createTable(Options options) { diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/Bridge.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/Bridge.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.lang.annotation.Repeatable; + +@Repeatable(Bridges.class) +@interface Bridge { + String value(); +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/BridgeHarness.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/BridgeHarness.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8013789 + * @summary Compiler should emit bridges in interfaces + * @library /tools/javac/lib + * @build JavacTestingAbstractProcessor BridgeHarness + * @run main BridgeHarness + */ + +import com.sun.source.util.JavacTask; +import com.sun.tools.classfile.AccessFlags; +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.ConstantPool; +import com.sun.tools.classfile.ConstantPoolException; +import com.sun.tools.classfile.Method; +import com.sun.tools.javac.code.Symbol.ClassSymbol; +import com.sun.tools.javac.util.List; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +import static javax.tools.StandardLocation.*; + +public class BridgeHarness { + + /** number of errors found (must be zero for the test to pass) */ + static int nerrors = 0; + + /** the (shared) Java compiler used for compiling the tests */ + static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); + + /** the (shared) file manager used by the compiler */ + static final StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); + + public static void main(String[] args) throws Exception { + //set sourcepath + fm.setLocation(SOURCE_PATH, + Arrays.asList(new File(System.getProperty("test.src"), "tests"))); + //set output (-d) + fm.setLocation(javax.tools.StandardLocation.CLASS_OUTPUT, + Arrays.asList(new File(System.getProperty("user.dir")))); + for (JavaFileObject jfo : fm.list(SOURCE_PATH, "", Collections.singleton(JavaFileObject.Kind.SOURCE), true)) { + //for each source, compile and check against annotations + new BridgeHarness(jfo).compileAndCheck(); + } + //if there were errors, fail + if (nerrors > 0) { + throw new AssertionError("Errors were found"); + } + } + + /* utility methods */ + + /** + * Remove an element from a list + */ + static List drop(List lz, Z z) { + if (lz.head == z) { + return drop(lz.tail, z); + } else if (lz.isEmpty()) { + return lz; + } else { + return drop(lz.tail, z).prepend(lz.head); + } + } + + /** + * return a string representation of a bytecode method + */ + static String descriptor(Method m, ConstantPool cp) throws ConstantPoolException { + return m.getName(cp) + m.descriptor.getValue(cp); + } + + /* test harness */ + + /** Test file to be compiled */ + JavaFileObject jfo; + + /** Mapping between class name and list of bridges in class with that name */ + Map> bridgesMap = new HashMap>(); + + protected BridgeHarness(JavaFileObject jfo) { + this.jfo = jfo; + } + + /** + * Compile a test using a custom annotation processor and check the generated + * bytecode against discovered annotations. + */ + protected void compileAndCheck() throws Exception { + JavacTask ct = (JavacTask)comp.getTask(null, fm, null, null, null, Arrays.asList(jfo)); + ct.setProcessors(Collections.singleton(new BridgeFinder())); + + for (JavaFileObject jfo : ct.generate()) { + checkBridges(jfo); + } + } + + /** + * Check that every bridge in the generated classfile has a matching bridge + * annotation in the bridge map + */ + protected void checkBridges(JavaFileObject jfo) { + try { + ClassFile cf = ClassFile.read(jfo.openInputStream()); + System.err.println("checking: " + cf.getName()); + + List bridgeList = bridgesMap.get(cf.getName()); + if (bridgeList == null) { + //no bridges - nothing to check; + bridgeList = List.nil(); + } + + for (Method m : cf.methods) { + if (m.access_flags.is(AccessFlags.ACC_SYNTHETIC | AccessFlags.ACC_BRIDGE)) { + //this is a bridge - see if there's a match in the bridge list + Bridge match = null; + for (Bridge b : bridgeList) { + if (b.value().equals(descriptor(m, cf.constant_pool))) { + match = b; + break; + } + } + if (match == null) { + error("No annotation for bridge method: " + descriptor(m, cf.constant_pool)); + } else { + bridgeList = drop(bridgeList, match); + } + } + } + if (bridgeList.nonEmpty()) { + error("Redundant bridge annotation found: " + bridgeList.head.value()); + } + } catch (Exception e) { + e.printStackTrace(); + throw new Error("error reading " + jfo.toUri() +": " + e); + } + } + + /** + * Log an error + */ + protected void error(String msg) { + nerrors++; + System.err.printf("Error occurred while checking file: %s\nreason: %s\n", jfo.getName(), msg); + } + + /** + * This annotation processor is used to populate the bridge map with the + * contents of the annotations that are found on the tests being compiled + */ + @SupportedAnnotationTypes({"Bridges","Bridge"}) + class BridgeFinder extends JavacTestingAbstractProcessor { + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) + return true; + + TypeElement bridgeAnno = elements.getTypeElement("Bridge"); + TypeElement bridgesAnno = elements.getTypeElement("Bridges"); + + //see if there are repeated annos + for (Element elem: roundEnv.getElementsAnnotatedWith(bridgesAnno)) { + List bridgeList = List.nil(); + Bridges bridges = elem.getAnnotation(Bridges.class); + for (Bridge bridge : bridges.value()) { + bridgeList = bridgeList.prepend(bridge); + } + bridgesMap.put(((ClassSymbol)elem).flatname.toString(), bridgeList); + } + + //see if there are non-repeated annos + for (Element elem: roundEnv.getElementsAnnotatedWith(bridgeAnno)) { + Bridge bridge = elem.getAnnotation(Bridge.class); + bridgesMap.put(((ClassSymbol)elem).flatname.toString(), + List.of(bridge)); + } + + return true; + } + } +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/Bridges.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/Bridges.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +@interface Bridges { + Bridge[] value(); +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/tests/TestBridgeWithDefault.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/tests/TestBridgeWithDefault.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +class TestBridgeWithDefault { + interface A { Object m(int x); } + + @Bridge("m(I)Ljava/lang/Object;") + interface B extends A { + String m(int x); + default Integer m(long x) { return null; } + } +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/tests/TestClassAndInterfaceBridgeIdentical01.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/tests/TestClassAndInterfaceBridgeIdentical01.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +class TestClassAndInterfaceBridgeIdentical01 { + + interface A { Object m(); } + interface B { Number m(); } + + @Bridge("m()Ljava/lang/Object;") + @Bridge("m()Ljava/lang/Number;") + interface C extends A, B { + Integer m(); + } + + @Bridge("m()Ljava/lang/Object;") + @Bridge("m()Ljava/lang/Number;") + static abstract class D implements A, B { + public abstract Integer m(); + } + + @Bridge("m()Ljava/lang/Object;") + @Bridge("m()Ljava/lang/Number;") + static class E implements A, B { + public Integer m() { return 1; } + } +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/tests/TestClassAndInterfaceBridgeIdentical02.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/tests/TestClassAndInterfaceBridgeIdentical02.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +class TestClassAndInterfaceBridgeIdentical02 { + + interface A { void m(X x); } + interface B { void m(X x); } + + @Bridge("m(Ljava/lang/Object;)V") + @Bridge("m(Ljava/lang/Number;)V") + interface C extends A, B { + void m(Integer i); + } + + @Bridge("m(Ljava/lang/Object;)V") + @Bridge("m(Ljava/lang/Number;)V") + static abstract class D implements A, B { + public abstract void m(Integer i); + } + + @Bridge("m(Ljava/lang/Object;)V") + @Bridge("m(Ljava/lang/Number;)V") + static class E implements A, B { + public void m(Integer i) { } + } +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/tests/TestNoBridgeInSiblingsSuper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/tests/TestNoBridgeInSiblingsSuper.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +class TestNoBridgeInSiblingSuper { + interface A { Object m(); } + interface B { String m(); } + //no bridge here! + interface C extends A, B { } + + @Bridge("m()Ljava/lang/Object;") + interface D extends C { + String m(); + } +} + diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/tests/TestNoDuplicateBridges01.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/tests/TestNoDuplicateBridges01.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +class TestNoDuplicateBridges01 { + interface A1 { Object m(); } + interface A2 { Object m(); } + + @Bridge("m()Ljava/lang/Object;") + interface B extends A1, A2 { B m(); } +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/generics/bridges/tests/TestNoDuplicateBridges02.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/bridges/tests/TestNoDuplicateBridges02.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +class TestNoDuplicateBridges02 { + interface A { + A get(); + } + + @Bridge("get()LTestNoDuplicateBridges02$A;") + interface B extends A { + B get(); + } + + @Bridge("get()LTestNoDuplicateBridges02$A;") + @Bridge("get()LTestNoDuplicateBridges02$B;") + interface C extends A, B { + C get(); + } +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/lambda/bridge/TestMetafactoryBridges.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambda/bridge/TestMetafactoryBridges.java Wed Jul 17 10:40:53 2013 -0700 @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8013789 + * @summary Compiler should emit bridges in interfaces + */ + +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.api.ClientCodeWrapper.DiagnosticSourceUnwrapper; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.util.JCDiagnostic; + +import java.io.File; +import java.io.PrintWriter; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +import javax.tools.Diagnostic; +import javax.tools.Diagnostic.Kind; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +public class TestMetafactoryBridges { + + static int checkCount = 0; + + enum ClasspathKind { + NONE(), + B7(7, ClassKind.B), + A7(7, ClassKind.A), + B8(8, ClassKind.B), + A8(8, ClassKind.A); + + int version; + ClassKind ck; + + ClasspathKind() { + this(-1, null); + } + + ClasspathKind(int version, ClassKind ck) { + this.version = version; + this.ck = ck; + } + } + + enum PreferPolicy { + SOURCE("-Xprefer:source"), + NEWER("-Xprefer:newer"); + + String preferOpt; + + PreferPolicy(String preferOpt) { + this.preferOpt = preferOpt; + } + } + + enum SourcepathKind { + NONE, + A(ClassKind.A), + B(ClassKind.B), + C(ClassKind.C), + AB(ClassKind.A, ClassKind.B), + BC(ClassKind.B, ClassKind.C), + AC(ClassKind.A, ClassKind.C), + ABC(ClassKind.A, ClassKind.B, ClassKind.C); + + List sources; + + SourcepathKind(ClassKind... sources) { + this.sources = Arrays.asList(sources); + } + } + + enum SourceSet { + ALL() { + @Override + List> permutations() { + return Arrays.asList( + Arrays.asList(ClassKind.A, ClassKind.B, ClassKind.C), + Arrays.asList(ClassKind.A, ClassKind.B, ClassKind.C), + Arrays.asList(ClassKind.B, ClassKind.A, ClassKind.C), + Arrays.asList(ClassKind.B, ClassKind.C, ClassKind.A), + Arrays.asList(ClassKind.C, ClassKind.A, ClassKind.B), + Arrays.asList(ClassKind.C, ClassKind.B, ClassKind.A) + ); + } + }, + AC() { + @Override + List> permutations() { + return Arrays.asList( + Arrays.asList(ClassKind.A, ClassKind.C), + Arrays.asList(ClassKind.C, ClassKind.A) + ); + } + }, + C() { + @Override + List> permutations() { + return Arrays.asList(Arrays.asList(ClassKind.C)); + } + }; + + abstract List> permutations(); + } + + enum ClassKind { + A("A", "interface A { Object m(); }"), + B("B", "interface B extends A { Integer m(); }", A), + C("C", "class C { B b = ()->42; }", A, B); + + String name; + String source; + ClassKind[] deps; + + ClassKind(String name, String source, ClassKind... deps) { + this.name = name; + this.source = source; + this.deps = deps; + } + } + + public static void main(String... args) throws Exception { + String SCRATCH_DIR = System.getProperty("user.dir"); + //create default shared JavaCompiler - reused across multiple compilations + JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); + + int n = 0; + for (SourceSet ss : SourceSet.values()) { + for (List sources : ss.permutations()) { + for (SourcepathKind spKind : SourcepathKind.values()) { + for (ClasspathKind cpKind : ClasspathKind.values()) { + for (PreferPolicy pp : PreferPolicy.values()) { + Set deps = EnumSet.noneOf(ClassKind.class); + if (cpKind.ck != null) { + deps.add(cpKind.ck); + } + deps.addAll(sources); + if (deps.size() < 3) continue; + File testDir = new File(SCRATCH_DIR, "test" + n); + testDir.mkdir(); + try (PrintWriter debugWriter = new PrintWriter(new File(testDir, "debug.txt"))) { + new TestMetafactoryBridges(testDir, sources, spKind, cpKind, pp, debugWriter).run(comp); + n++; + } + } + } + } + } + } + System.out.println("Total check executed: " + checkCount); + } + + File testDir; + List sources; + SourcepathKind spKind; + ClasspathKind cpKind; + PreferPolicy pp; + PrintWriter debugWriter; + DiagnosticChecker diagChecker; + + TestMetafactoryBridges(File testDir, Listsources, SourcepathKind spKind, + ClasspathKind cpKind, PreferPolicy pp, PrintWriter debugWriter) { + this.testDir = testDir; + this.sources = sources; + this.spKind = spKind; + this.cpKind = cpKind; + this.pp = pp; + this.debugWriter = debugWriter; + this.diagChecker = new DiagnosticChecker(); + } + + class JavaSource extends SimpleJavaFileObject { + + final String source; + + public JavaSource(ClassKind ck) { + super(URI.create(String.format("myfo:/%s.java", ck.name)), JavaFileObject.Kind.SOURCE); + this.source = ck.source; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return source; + } + } + + void run(JavaCompiler tool) throws Exception { + File classesDir = new File(testDir, "classes"); + File outDir = new File(testDir, "out"); + File srcDir = new File(testDir, "src"); + classesDir.mkdir(); + outDir.mkdir(); + srcDir.mkdir(); + + debugWriter.append(testDir.getName() + "\n"); + debugWriter.append("sources = " + sources + "\n"); + debugWriter.append("spKind = " + spKind + "\n"); + debugWriter.append("cpKind = " + cpKind + "\n"); + debugWriter.append("preferPolicy = " + pp.preferOpt + "\n"); + + //step 1 - prepare sources (older!!) + debugWriter.append("Preparing sources\n"); + for (ClassKind ck : spKind.sources) { + //skip sources explicitly provided on command line + if (!sources.contains(ck)) { + debugWriter.append("Copy " + ck.name + ".java to" + srcDir.getAbsolutePath() + "\n"); + File dest = new File(srcDir, ck.name + ".java"); + PrintWriter pw = new PrintWriter(dest); + pw.append(ck.source); + pw.close(); + } + } + + //step 2 - prepare classes + debugWriter.append("Preparing classes\n"); + if (cpKind != ClasspathKind.NONE) { + List sources = new ArrayList<>(); + ClassKind toRemove = null; + sources.add(new JavaSource(cpKind.ck)); + if (cpKind.ck.deps.length != 0) { + //at most only one dependency + toRemove = cpKind.ck.deps[0]; + sources.add(new JavaSource(toRemove)); + } + JavacTask ct = (JavacTask)tool.getTask(debugWriter, null, null, + Arrays.asList("-d", classesDir.getAbsolutePath(), "-source", String.valueOf(cpKind.version)), null, sources); + try { + ct.generate(); + if (toRemove != null) { + debugWriter.append("Remove " + toRemove.name + ".class from" + classesDir.getAbsolutePath() + "\n"); + File fileToRemove = new File(classesDir, toRemove.name + ".class"); + fileToRemove.delete(); + } + } catch (Throwable ex) { + throw new AssertionError("Error thrown when generating side-classes"); + } + } + + //step 3 - compile + debugWriter.append("Compiling test\n"); + List sourcefiles = new ArrayList<>(); + for (ClassKind ck : sources) { + sourcefiles.add(new JavaSource(ck)); + } + JavacTask ct = (JavacTask)tool.getTask(debugWriter, null, diagChecker, + Arrays.asList("-XDdumpLambdaToMethodStats", "-d", outDir.getAbsolutePath(), + "-sourcepath", srcDir.getAbsolutePath(), + "-classpath", classesDir.getAbsolutePath(), + pp.preferOpt), null, sourcefiles); + try { + ct.generate(); + } catch (Throwable ex) { + throw new AssertionError("Error thrown when compiling test case"); + } + check(); + } + + void check() { + checkCount++; + if (diagChecker.errorFound) { + throw new AssertionError("Unexpected compilation failure"); + } + + boolean altMetafactory = + cpKind == ClasspathKind.B7 && + !sources.contains(ClassKind.B) && + (pp == PreferPolicy.NEWER || !spKind.sources.contains(ClassKind.B)); + + if (altMetafactory != diagChecker.altMetafactory) { + throw new AssertionError("Bad metafactory detected - expected altMetafactory: " + altMetafactory + + "\ntest: " + testDir); + } + } + + static class DiagnosticChecker implements javax.tools.DiagnosticListener { + + boolean altMetafactory = false; + boolean errorFound = false; + + public void report(Diagnostic diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } else if (statProcessor.matches(diagnostic)) { + statProcessor.process(diagnostic); + } + } + + abstract class DiagnosticProcessor { + + List codes; + Diagnostic.Kind kind; + + public DiagnosticProcessor(Kind kind, String... codes) { + this.codes = Arrays.asList(codes); + this.kind = kind; + } + + abstract void process(Diagnostic diagnostic); + + boolean matches(Diagnostic diagnostic) { + return (codes.isEmpty() || codes.contains(diagnostic.getCode())) && + diagnostic.getKind() == kind; + } + + JCDiagnostic asJCDiagnostic(Diagnostic diagnostic) { + if (diagnostic instanceof JCDiagnostic) { + return (JCDiagnostic)diagnostic; + } else if (diagnostic instanceof DiagnosticSourceUnwrapper) { + return ((DiagnosticSourceUnwrapper)diagnostic).d; + } else { + throw new AssertionError("Cannot convert diagnostic to JCDiagnostic: " + diagnostic.getClass().getName()); + } + } + } + + DiagnosticProcessor statProcessor = new DiagnosticProcessor(Kind.NOTE, + "compiler.note.lambda.stat", + "compiler.note.mref.stat", + "compiler.note.mref.stat.1") { + @Override + void process(Diagnostic diagnostic) { + JCDiagnostic diag = asJCDiagnostic(diagnostic); + if ((Boolean)diag.getArgs()[0]) { + altMetafactory = true; + } + } + }; + } +} diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/lambda/lambdaExpression/LambdaTest6.java --- a/test/tools/javac/lambda/lambdaExpression/LambdaTest6.java Wed Jul 17 15:08:58 2013 +0200 +++ b/test/tools/javac/lambda/lambdaExpression/LambdaTest6.java Wed Jul 17 10:40:53 2013 -0700 @@ -105,7 +105,7 @@ Class returnType = m.getReturnType(); assertTrue(types.remove(returnType.getName())); } - assertTrue(types.isEmpty()); + assertTrue(types.size() == 1); //there's a bridge } diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/lambda/methodReference/BridgeMethod.java --- a/test/tools/javac/lambda/methodReference/BridgeMethod.java Wed Jul 17 15:08:58 2013 +0200 +++ b/test/tools/javac/lambda/methodReference/BridgeMethod.java Wed Jul 17 10:40:53 2013 -0700 @@ -112,6 +112,6 @@ Class returnType = m.getReturnType(); assertTrue(types.remove(returnType.getName())); } - assertTrue(types.isEmpty()); + assertTrue(types.size() == 1); //there's a bridge } } diff -r 10711bd8bb2d -r e990e6bcecbe test/tools/javac/lambdaShapes/org/openjdk/tests/vm/DefaultMethodsTest.java --- a/test/tools/javac/lambdaShapes/org/openjdk/tests/vm/DefaultMethodsTest.java Wed Jul 17 15:08:58 2013 +0200 +++ b/test/tools/javac/lambdaShapes/org/openjdk/tests/vm/DefaultMethodsTest.java Wed Jul 17 10:40:53 2013 -0700 @@ -395,6 +395,7 @@ * TEST: C c = new C(); c.m() == 88; * TEST: I i = new C(); i.m() == 88; */ + @Test(enabled=false) public void testSelfFill() { // This test ensures that a concrete method overrides a default method // that matches at the language-level, but has a different method @@ -484,6 +485,7 @@ * TEST: J j = new C(); j.m("A","B","C") == 88; * TEST: K k = new C(); k.m("A","B","C") == 88; */ + @Test(enabled=false) public void testBridges() { DefaultMethod dm = new DefaultMethod("int", stdMethodName, "return 99;", new MethodParameter("T", "t"), new MethodParameter("V", "v"), @@ -672,6 +674,7 @@ * class S { Object foo() { return (new D()).m(); } // link sig: ()LInteger; * TEST: S s = new S(); s.foo() == new Integer(99) */ + @Test(enabled=false) public void testCovarBridge() { Interface I = new Interface("I", new DefaultMethod( "Integer", "m", "return new Integer(88);")); @@ -754,6 +757,7 @@ * Test that a erased-signature-matching method does not implement * non-language-level matching methods */ + @Test(enabled=false) public void testNonConcreteFill() { AbstractMethod ipm = new AbstractMethod("int", "m", new MethodParameter("T", "t"),