changeset 3280:2bb582ce72c4

Enhancement: add initial support for alternate generic method translation scheme
author mcimadamore
date Tue, 22 Dec 2015 14:59:35 +0000
parents 71e8f37f46ae
children dddf65a2e118
files src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/SpecializeTypes.java test/tools/javac/valhalla/typespec/GenericMethod01.java
diffstat 3 files changed, 266 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Fri Dec 18 18:24:00 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Tue Dec 22 14:59:35 2015 +0000
@@ -208,6 +208,8 @@
     public final Type elementTypeType;
     public final Type functionalInterfaceType;
     public final Type genericMethodSpecialzer;
+    public final Type genericInstanceDispatch;
+    public final Type genericStaticDispatch;
     public final Type virtualAccess;
 
     /** The symbol representing the length field of an array.
@@ -533,6 +535,8 @@
         lambdaMetafactory = enterClass("java.lang.invoke.LambdaMetafactory");
         functionalInterfaceType = enterClass("java.lang.FunctionalInterface");
         genericMethodSpecialzer = enterClass("java.lang.invoke.GenericMethodSpecializer");
+        genericInstanceDispatch = enterClass("java.lang.invoke.GenericInstanceDispatch");
+        genericStaticDispatch = enterClass("java.lang.invoke.GenericStaticDispatch");
         virtualAccess = enterClass("java.lang.invoke.VirtualAccess");
 
         //transitional
@@ -546,6 +550,8 @@
         synthesizeEmptyInterfaceIfMissing(serializableType);
         synthesizeEmptyInterfaceIfMissing(lambdaMetafactory);
         synthesizeEmptyBSMIfMissing(genericMethodSpecialzer);
+        synthesizeEmptyBSMIfMissing(genericInstanceDispatch);
+        synthesizeEmptyBSMIfMissing(genericStaticDispatch);
         synthesizeEmptyBSMIfMissing(virtualAccess);
         synthesizeEmptyInterfaceIfMissing(serializedLambdaType);
         synthesizeBoxTypeIfMissing(doubleType);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/SpecializeTypes.java	Fri Dec 18 18:24:00 2015 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/SpecializeTypes.java	Tue Dec 22 14:59:35 2015 +0000
@@ -26,18 +26,23 @@
 package com.sun.tools.javac.comp;
 
 import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Flags.Flag;
+import com.sun.tools.javac.code.Scope.WriteableScope;
 import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol.BootstrapArgument;
 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol.BootstrapArgument.Kind;
 import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
+import com.sun.tools.javac.code.Symbol.TypeVariableSymbol;
 import com.sun.tools.javac.code.Symbol.VarSymbol;
 import com.sun.tools.javac.code.Symtab;
 import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.code.Type.ClassType;
 import com.sun.tools.javac.code.Type.ForAll;
 import com.sun.tools.javac.code.Type.MethodType;
+import com.sun.tools.javac.code.Type.TypeVar;
 import com.sun.tools.javac.code.TypeTag;
 import com.sun.tools.javac.code.Types;
 import com.sun.tools.javac.code.Types.TypeVarContext;
@@ -55,6 +60,8 @@
 import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
 import com.sun.tools.javac.tree.JCTree.JCIdent;
 import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
+import com.sun.tools.javac.tree.JCTree.JCLambda;
+import com.sun.tools.javac.tree.JCTree.JCMemberReference;
 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
 import com.sun.tools.javac.tree.JCTree.JCNewArray;
@@ -74,6 +81,7 @@
 import com.sun.tools.javac.util.ListBuffer;
 import com.sun.tools.javac.util.Log;
 import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Options;
 import com.sun.tools.javac.util.Tuple.Tuple2;
 
 import java.util.HashMap;
@@ -119,6 +127,8 @@
     private CompileStates compileStates;
     private Log log;
 
+    private boolean desugarGenericMethods;
+
     private SpecializeTypes(Context context) {
         super(context);
         context.put(specializeTypesKey, this);
@@ -126,6 +136,8 @@
         enter = Enter.instance(context);
         compileStates = CompileStates.instance(context);
         log = Log.instance(context);
+        Options options = Options.instance(context);
+        desugarGenericMethods = options.isSet("desugarGenericMethods");
     }
 
     /** class currently being specialized. */
@@ -227,6 +239,7 @@
                                                List<JCExpression> args, JCExpression receiverExpr,
                                                List<Type> typeargs, Type receiverType, Type varargsElement) {
         if (typeargs.nonEmpty()) {
+            boolean altTranslation = desugarGenericMethods;
             if (typeargs.stream().anyMatch(types::isTypeArgumentSpecializable)) {
                 if (!receiverType.hasTag(NONE)) {
                     receiverType = types.decorateDescriptor(receiverType, receiverType.tsym.type);
@@ -250,17 +263,20 @@
                 Symbol unspecializedMeth = msym.baseSymbol();
 
                 staticArgs = staticArgs.prepend(BootstrapArgument.MethodHandle((MethodSymbol)unspecializedMeth));
+
                 //structural info is not required for the generic specialized call case
                 //as there, we should leave the method handle unspecialized, as it should point
                 //to the original version of the method in the template class.
                 staticArgs.head.flags &= ~BootstrapArgument.SPECIALIZABLE;
+
+                //this is ignored for alt translation
                 Type effFinalReceiverType = receiverType;
                 staticArgs = staticArgs.prepend(new BootstrapArgument<String>(Kind.STRING, types.bytecodeMappingSig(receiverType).toString()) {
-                                                    @Override
-                                                    public Optional<Type> asType() {
-                                                        return Optional.of(effFinalReceiverType);
-                                                    }
-                                                });
+                    @Override
+                    public Optional<Type> asType() {
+                        return Optional.of(effFinalReceiverType);
+                    }
+                });
 
                 Type declared = msym.type;
                 if (!receiverType.hasTag(TypeTag.NONE)) {
@@ -312,7 +328,13 @@
                     indyType = types.createMethodTypeWithReturn(indyType, receiverType);
                 }
 
-                JCMethodInvocation methInv = makeIndyCall(tree, msym.owner, syms.genericMethodSpecialzer,
+                Type bsmType = syms.genericMethodSpecialzer;
+                if (altTranslation) {
+                    bsmType = msym.isStatic() ?
+                            syms.genericStaticDispatch :
+                            syms.genericInstanceDispatch;
+                }
+                JCMethodInvocation methInv = makeIndyCall(tree, msym.owner, bsmType,
                         names.metafactory, staticArgs, (MethodType)indyType, args, msym.name, msym);
                 methInv.varargsElement = varargsElement;
                 methInv.unerasedType = tree.type;
@@ -400,6 +422,11 @@
 
     @Override
     public void visitMethodDef(JCMethodDecl tree) {
+        if (desugarGenericMethods && tree.typarams != null &&
+                tree.typarams.stream().anyMatch(p -> types.isAnyTypeVar(p.type))) {
+            //desugar specializable method to inner classes (only static for now)
+            tree = makeGenericMethodClass(tree);
+        }
         if (TreeInfo.isPeeledMethod(tree)) {
             for (JCStatement stat : tree.body.stats) {
                 JCBlock whereBlock = (JCBlock)stat;
@@ -680,7 +707,7 @@
                     .anyMatch(p -> p.elem0.kind == MTH && p.elem1.stream().anyMatch(types::isAnyTypeVar))) {
                 log.warning(tree, "valhalla.not.supported", "class with implicit any type-vars");
             }
-            if (types.isAnyfied(tree.sym)) {
+            if (types.isAnyfied(tree.sym) && (tree.sym.flags() & Flags.SYNTHETIC) == 0) {
                 //generate companion virtual interface
                 JCClassDecl virtualIntf = makeVirtualInterface(tree.sym);
                 for (Symbol s : virtualIntf.sym.members().getSymbols()) {
@@ -750,6 +777,167 @@
     }
 
     /**
+     * Desugar a generic specializable method into a specializable inner class + instance method pair.
+     */
+    JCMethodDecl makeGenericMethodClass(JCMethodDecl tree) {
+        MethodSymbol msym = tree.sym;
+
+        //create generic inner class symbol
+        ClassSymbol genMethodClassSym = new ClassSymbol(Flags.SYNTHETIC | msym.flags(),
+                currentClass.getSimpleName().append('$', msym.name),
+                null,
+                currentClass);
+        genMethodClassSym.flags_field &= ~Flags.AccessFlags;
+        genMethodClassSym.flags_field |= Flags.PUBLIC;
+
+        //compute old vs. new type parameters
+        List<Type> oldTypeParams = tree.typarams.stream()
+                .map(p -> p.type)
+                .collect(List.collector());
+
+        List<Type> newTypeParams = oldTypeParams.stream()
+                .map(tv -> {
+                    TypeVariableSymbol tvsym = new TypeVariableSymbol(tv.tsym.flags(), tv.tsym.name, null, genMethodClassSym);
+                    return tvsym.type = new TypeVar(tvsym, tv.getUpperBound(), null);
+                }).collect(List.collector());
+
+        types.substBounds(newTypeParams, oldTypeParams, newTypeParams);
+
+        //init generic inner class type
+        genMethodClassSym.type = new ClassType(msym.isStatic() ? Type.noType : currentClass.type,
+                newTypeParams,
+                genMethodClassSym);
+        ((ClassType)genMethodClassSym.type).supertype_field = syms.objectType;
+        ((ClassType)genMethodClassSym.type).interfaces_field = List.nil();
+        genMethodClassSym.sourcefile = currentClass.sourcefile;
+
+        //desugar generic method as instance method of the fresh inner class
+        MethodSymbol desugaredMethod = msym.clone(genMethodClassSym);
+        desugaredMethod.flags_field &= ~Flags.AccessFlags;
+        desugaredMethod.flags_field &= ~Flags.STATIC;
+        desugaredMethod.flags_field |= Flags.PUBLIC;
+        desugaredMethod.type = types.subst(msym.type.asMethodType(), oldTypeParams, newTypeParams);
+        genMethodClassSym.members_field = WriteableScope.create(genMethodClassSym);
+        genMethodClassSym.members().enter(desugaredMethod);
+
+        JCMethodDecl desugaredMethodDecl = make.MethodDef(desugaredMethod, tree.body);
+        new GenericMethodPatcher(tree, desugaredMethodDecl, oldTypeParams, newTypeParams).scan(desugaredMethodDecl);
+
+        //create a default constructor for the inner class
+        MethodSymbol defaultConstr = new MethodSymbol(Flags.PUBLIC, names.init,
+                new MethodType(List.nil(), syms.voidType, List.nil(), syms.methodClass),
+                genMethodClassSym);
+        genMethodClassSym.members().enter(defaultConstr);
+
+        JCMethodDecl defaultConstrDecl = make.MethodDef(defaultConstr, null);
+        JCIdent rcvr = make.Ident(names._super);
+        rcvr.sym = syms.objectType.tsym;
+        rcvr.type = syms.objectType;
+        JCFieldAccess meth = make.Select(rcvr, names.init);
+        Symbol objectInit = rs.resolveInternalMethod(tree, attrEnv, syms.objectType, names.init, List.nil(), List.nil());
+        meth.type = objectInit.type;
+        meth.sym = objectInit;
+        defaultConstrDecl.body = make.Block(0, List.of(make.Exec(make.App(meth).setType(syms.voidType))));
+
+        //create inner class declaration
+        JCClassDecl desugaredClassDecl = make.ClassDef(
+                make.Modifiers(genMethodClassSym.flags()),
+                genMethodClassSym.name,
+                make.TypeParams(newTypeParams),
+                null,
+                List.nil(),
+                List.of(defaultConstrDecl, desugaredMethodDecl));
+        desugaredClassDecl.sym = genMethodClassSym;
+        desugaredClassDecl.type = genMethodClassSym.type;
+        Env<AttrContext> env1 = enter.typeEnvs.get(currentClass).dup(desugaredClassDecl);
+        enter.typeEnvs.put(genMethodClassSym, env1);
+        compileStates.put(env1, CompileState.FLOW);
+        pendingDefs = pendingDefs.prepend(desugaredClassDecl);
+        currentClass.members().enter(genMethodClassSym);
+        translate(desugaredClassDecl);
+
+        //create bridge
+        JCMethodDecl bridgeDecl = make.MethodDef(msym, null);
+        JCNewClass newClazz = make.NewClass(null, List.nil(), make.Ident(genMethodClassSym), List.nil(), null);
+        newClazz.constructor = defaultConstr;
+        newClazz.constructorType = defaultConstr.type;
+        newClazz.type = genMethodClassSym.type;
+        JCFieldAccess bridgedCall = make.Select(newClazz, msym.name);
+        bridgedCall.type = types.erasure(desugaredMethod.type);
+        bridgedCall.sym = desugaredMethod;
+        List<JCExpression> args = bridgeDecl.params.stream()
+                .map(p -> make.Ident(p.sym)).collect(List.collector());
+        JCStatement stat = msym.type.getReturnType().hasTag(VOID) ?
+                make.Exec(make.App(bridgedCall, args).setType(syms.voidType)) :
+                make.Return(make.App(bridgedCall, args).setType(types.erasure(msym.type.getReturnType())));
+        bridgeDecl.body = make.Block(0, List.of(stat));
+        return bridgeDecl;
+    }
+    //where
+        class GenericMethodPatcher extends TreeScanner {
+            Map<Symbol, Symbol> paramMap;
+            List<Type> oldTypeVars, newTypeVars;
+
+            GenericMethodPatcher(JCMethodDecl oldMeth, JCMethodDecl newMeth,
+                                 List<Type> oldTypeVars, List<Type> newTypeVars) {
+                paramMap = Tuple2.zip(oldMeth.params, newMeth.params).stream()
+                        .collect(Collectors.toMap(p -> p.elem0.sym, p -> p.elem1.sym));
+                this.oldTypeVars = oldTypeVars;
+                this.newTypeVars = newTypeVars;
+            }
+
+            @Override
+            public void scan(JCTree tree) {
+                super.scan(tree);
+                if (tree != null && tree.type != null) {
+                    tree.type = patchTypeVars(tree.type);
+                }
+            }
+
+            @Override
+            public void visitNewClass(JCNewClass tree) {
+                super.visitNewClass(tree);
+                tree.constructorType = patchTypeVars(tree.constructorType);
+            }
+
+            @Override
+            public void visitLambda(JCLambda tree) {
+                super.visitLambda(tree);
+                tree.targets = tree.targets.stream()
+                        .map(this::patchTypeVars).collect(List.collector());
+            }
+
+            @Override
+            public void visitReference(JCMemberReference tree) {
+                super.visitReference(tree);
+                tree.targets = tree.targets.stream()
+                        .map(this::patchTypeVars).collect(List.collector());
+                tree.referentSite = patchTypeVars(tree.referentSite);
+                tree.referentType = patchTypeVars(tree.referentType);
+            }
+
+            @Override
+            public void visitIdent(JCIdent tree) {
+                super.visitIdent(tree);
+                tree.sym = patchParam(tree.sym);
+            }
+
+            @Override
+            public void visitSelect(JCFieldAccess tree) {
+                super.visitSelect(tree);
+                tree.sym = patchParam(tree.sym);
+            }
+
+            Type patchTypeVars(Type t) {
+                return types.subst(t, oldTypeVars, newTypeVars);
+            }
+
+            Symbol patchParam(Symbol s) {
+                return paramMap.getOrDefault(s, s);
+            }
+        }
+
+    /**
      * Generate a tree corresponding to a virtual (synthetic) interface. Such an interface contains
      * two things: (i) boxing getters/setters for the anyfied class fields; (ii) boxing bridges for
      * the anyfied class methods.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/valhalla/typespec/GenericMethod01.java	Tue Dec 22 14:59:35 2015 +0000
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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
+ * @summary smoke test for generic methods (suaing both translation schemes)
+ * @clean .*
+ * @compile GenericMethod01.java
+ * @run main GenericMethod01
+ * @clean .*
+ * @compile -XDdesugarGenericMethods GenericMethod01.java
+ * @run main GenericMethod01
+ */
+public class GenericMethod01 {
+
+    static class Foo<any X> {
+        static <any Z> Z m_static(Z z) {
+            return z;
+        }
+
+        <any Z> Z m_inst(Z z) {
+            System.err.println("Foo");
+            return z;
+        }
+    }
+
+
+    static class Bar<any X> extends Foo<X> {
+        <any Z> Z m_inst(Z z) {
+            System.err.println("Bar");
+            return z;
+        }
+    }
+
+    public static void main(String[] args) {
+        int i1 = Foo.m_static(42);
+        String s1 = Foo.m_static("");
+        int i2 = new Bar<Object>().m_inst(42);
+        String s2 = new Bar<Object>().m_inst("");
+        int i3 = new Bar<long>().m_inst(42);
+        String s3 = new Bar<long>().m_inst("");
+    }
+}