changeset 54575:8e2f2d280e44 condy-lambda

generate condy for non-capturing lambda bulk push
author vromero
date Thu, 24 Jan 2019 18:31:17 -0500
parents 94b6130d6fe6
children 313b394c43de
files make/CompileJavaModules.gmk make/GenerateLinkOptData.gmk src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Items.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Pool.java src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java test/langtools/tools/javac/condy/CheckCondyGeneratedForLambdaTest.java test/langtools/tools/javac/condy/CheckForCondyDuplicatesTest.java test/langtools/tools/javac/condy/LambdaSerializationTest.java
diffstat 17 files changed, 748 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/make/CompileJavaModules.gmk	Thu Jan 24 22:08:03 2019 +0100
+++ b/make/CompileJavaModules.gmk	Thu Jan 24 18:31:17 2019 -0500
@@ -38,7 +38,7 @@
 ################################################################################
 # Module specific build settings
 
-java.base_ADD_JAVAC_FLAGS += -Xdoclint:all/protected,-reference '-Xdoclint/package:java.*,javax.*' -XDstringConcat=inline
+java.base_ADD_JAVAC_FLAGS += -Xdoclint:all/protected,-reference '-Xdoclint/package:java.*,javax.*' -XDstringConcat=inline -XDforNonCapturingLambda=generateIndy
 java.base_COPY += .icu .dat .spp content-types.properties hijrah-config-islamic-umalqura.properties
 java.base_CLEAN += intrinsic.properties
 
@@ -260,7 +260,7 @@
 
 ################################################################################
 
-java.rmi_ADD_JAVAC_FLAGS += -Xdoclint:all/protected '-Xdoclint/package:java.*,javax.*'
+java.rmi_ADD_JAVAC_FLAGS += -Xdoclint:all/protected '-Xdoclint/package:java.*,javax.*' -XDforNonCapturingLambda=generateIndy
 java.rmi_CLEAN_FILES += $(wildcard \
     $(TOPDIR)/src/java.rmi/share/classes/sun/rmi/registry/resources/*.properties \
     $(TOPDIR)/src/java.rmi/share/classes/sun/rmi/server/resources/*.properties)
--- a/make/GenerateLinkOptData.gmk	Thu Jan 24 22:08:03 2019 +0100
+++ b/make/GenerateLinkOptData.gmk	Thu Jan 24 18:31:17 2019 -0500
@@ -43,6 +43,7 @@
     INCLUDES := build/tools/classlist, \
     BIN := $(BUILDTOOLS_OUTPUTDIR)/classlist_classes, \
     JAR := $(SUPPORT_OUTPUTDIR)/classlist.jar, \
+    ADD_JAVAC_FLAGS := -XDforNonCapturingLambda=generateIndy, \
 ))
 
 TARGETS += $(CLASSLIST_JAR)
--- a/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Thu Jan 24 18:31:17 2019 -0500
@@ -200,6 +200,17 @@
             throws LambdaConversionException;
 
     /**
+     * Builds an instance of the functional interface directly.
+     *
+     * @return an instance of the functional interface
+     * @throws ReflectiveOperationException
+     * @throws LambdaConversionException If properly formed functional interface
+     * is not found or if creating the functional interface requires parameters
+     */
+    abstract Object buildFunctionalInterfaceInstance()
+            throws LambdaConversionException;
+
+    /**
      * Check the meta-factory arguments for errors
      * @throws LambdaConversionException if there are improper conversions
      */
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Thu Jan 24 18:31:17 2019 -0500
@@ -188,26 +188,8 @@
     CallSite buildCallSite() throws LambdaConversionException {
         final Class<?> innerClass = spinInnerClass();
         if (invokedType.parameterCount() == 0) {
-            final Constructor<?>[] ctrs = AccessController.doPrivileged(
-                    new PrivilegedAction<>() {
-                @Override
-                public Constructor<?>[] run() {
-                    Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
-                    if (ctrs.length == 1) {
-                        // The lambda implementing inner class constructor is private, set
-                        // it accessible (by us) before creating the constant sole instance
-                        ctrs[0].setAccessible(true);
-                    }
-                    return ctrs;
-                }
-                    });
-            if (ctrs.length != 1) {
-                throw new LambdaConversionException("Expected one lambda constructor for "
-                        + innerClass.getCanonicalName() + ", got " + ctrs.length);
-            }
-
             try {
-                Object inst = ctrs[0].newInstance();
+                Object inst = getConstructor(innerClass).newInstance();
                 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
             }
             catch (ReflectiveOperationException e) {
@@ -227,6 +209,55 @@
     }
 
     /**
+     * Builds an instance of the functional interface directly.
+     *
+     * Generate a class file which implements the functional interface, define
+     * the class, create an instance of the class.
+     *
+     * @return an instance of the functional interface
+     * @throws ReflectiveOperationException
+     * @throws LambdaConversionException If properly formed functional interface
+     * is not found or if the functional interface expects parameters
+     */
+    @Override
+    Object buildFunctionalInterfaceInstance() throws LambdaConversionException {
+        if (invokedType.parameterCount() == 0) {
+            final Class<?> innerClass = spinInnerClass();
+            try {
+                return getConstructor(innerClass).newInstance();
+            }
+            catch (ReflectiveOperationException e) {
+                throw new LambdaConversionException("Exception instantiating lambda object", e);
+            }
+        } else {
+            throw new LambdaConversionException("Building functional interface instances directly " +
+                    "only supported when there are no parameters");
+        }
+    }
+
+    private Constructor<?> getConstructor(Class<?> innerClass) throws LambdaConversionException {
+        final Constructor<?>[] ctrs = AccessController.doPrivileged(
+                new PrivilegedAction<>() {
+                    @Override
+                    public Constructor<?>[] run() {
+                        Constructor<?>[] ctrs1 = innerClass.getDeclaredConstructors();
+                        if (ctrs1.length == 1) {
+                            // The lambda implementing inner class constructor is private, set
+                            // it accessible (by us) before creating the constant sole instance
+                            ctrs1[0].setAccessible(true);
+                        }
+                        return ctrs1;
+                    }
+                });
+
+        if (ctrs.length != 1) {
+            throw new LambdaConversionException("Expected one lambda constructor for "
+                    + innerClass.getCanonicalName() + ", got " + ctrs.length);
+        }
+        return ctrs[0];
+    }
+
+    /**
      * Generate a class file which implements the functional
      * interface, define and return the class.
      *
--- a/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaMetafactory.java	Thu Jan 24 18:31:17 2019 -0500
@@ -330,6 +330,71 @@
     }
 
     /**
+     * Special-case case version of {@link LambdaMetafactory#metafactory(MethodHandles.Lookup, String, Class, MethodType, MethodHandle, MethodType)}
+     * that is restricted to non-capturing lambdas.  Rather than returning a
+     * {@link CallSite}, the function object itself is returned.
+     * Typically used as a <em>bootstrap method</em> for {@code Dynamic}
+     * constants, to support the <em>lambda expression</em> and <em>method
+     * reference expression</em> features of the Java Programming Language.
+     *
+     * <p>The function object returned is an instance of a class which
+     * implements the interface named by {@code functionalInterface},
+     * declares a method with the name given by {@code invokedName} and the
+     * signature given by {@code samMethodType}.  It may also override additional
+     * methods from {@code Object}.
+     *
+     * @param caller Represents a lookup context with the accessibility
+     *               privileges of the caller.  When used with {@code invokedynamic},
+     *               this is stacked automatically by the VM.
+     * @param invokedName The name of the method to implement.  When used with
+     *                    {@code Dynamic} constants, this is provided by the
+     *                    {@code NameAndType} of the {@code InvokeDynamic}
+     *                    structure and is stacked automatically by the VM.
+     * @param functionalInterface The functional interface the function object
+     *                            should implement.  When used with {@code invokedynamic},
+     *                            this is provided by the {@code NameAndType} of
+     *                            the {@code InvokeDynamic} structure and is
+     *                            stacked automatically by the VM. In the event
+     *                            that the implementation method is an instance
+     *                            method and this signature has any parameters,
+     *                            the first parameter in the invocation signature
+     *                            must correspond to the receiver.
+     * @param samMethodType Signature and return type of method to be implemented
+     *                      by the function object.
+     * @param implMethod A direct method handle describing the implementation
+     *                   method which should be called (with suitable adaptation
+     *                   of argument types, return types, and with captured
+     *                   arguments prepended to the invocation arguments) at
+     *                   invocation time.
+     * @param instantiatedMethodType The signature and return type that should
+     *                               be enforced dynamically at invocation time.
+     *                               This may be the same as {@code samMethodType},
+     *                               or may be a specialization of it.
+     * @return a CallSite whose target can be used to perform capture, generating
+     *         instances of the interface named by {@code invokedType}
+     * @throws LambdaConversionException If any of the linkage invariants
+     *                                   described {@link LambdaMetafactory above}
+     *                                   are violated
+     */
+    public static Object metafactory(MethodHandles.Lookup caller,
+                                     String invokedName,
+                                     Class<?> functionalInterface,
+                                     MethodType samMethodType,
+                                     MethodHandle implMethod,
+                                     MethodType instantiatedMethodType)
+            throws LambdaConversionException {
+        AbstractValidatingLambdaMetafactory mf;
+        mf = new InnerClassLambdaMetafactory(caller, MethodType.methodType(functionalInterface),
+                                             invokedName, samMethodType,
+                                             implMethod, instantiatedMethodType,
+                                             false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
+        mf.validateMetafactoryArgs();
+        return mf.buildFunctionalInterfaceInstance();
+    }
+
+    // @@@ Special case version of altMetafactory, supporting FLAG_METHODREF
+
+    /**
      * Facilitates the creation of simple "function objects" that implement one
      * or more interfaces by delegation to a provided {@link MethodHandle},
      * after appropriate type adaptation and partial evaluation of arguments.
@@ -503,4 +568,61 @@
         mf.validateMetafactoryArgs();
         return mf.buildCallSite();
     }
+
+    /**
+     * Special-case case version of {@link LambdaMetafactory#altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)}.
+     * Rather than returning a {@link CallSite}, the function object itself is returned.
+     * Typically used as a <em>bootstrap method</em> for {@code Dynamic}
+     * constants, to support the <em>lambda expression</em> and <em>method
+     * reference expression</em> features of the Java Programming Language.
+     *
+     * <p>The function object returned is an instance of a class which
+     * implements the interface named by {@code functionalInterface},
+     * declares a method with the name given by {@code invokedName} and the
+     * signature given by {@code samMethodType}.  It may also override additional
+     * methods from {@code Object}.
+     *
+     * @param caller              Represents a lookup context with the accessibility
+     *                            privileges of the caller.  When used with {@code invokedynamic},
+     *                            this is stacked automatically by the VM.
+     * @param invokedName         The name of the method to implement.  When used with
+     *                            {@code Dynamic} constants, this is provided by the
+     *                            {@code NameAndType} of the {@code InvokeDynamic}
+     *                            structure and is stacked automatically by the VM.
+     * @param functionalInterface The functional interface the function object
+     *                            should implement.  When used with {@code invokedynamic},
+     *                            this is provided by the {@code NameAndType} of
+     *                            the {@code InvokeDynamic} structure and is
+     *                            stacked automatically by the VM. In the event
+     *                            that the implementation method is an instance
+     *                            method and this signature has any parameters,
+     *                            the first parameter in the invocation signature
+     *                            must correspond to the receiver.
+     * @param args                An {@code Object[]} array containing the required
+     *                            arguments {@code samMethodType}, {@code implMethod},
+     *                            {@code instantiatedMethodType}, {@code flags}, and any
+     *                            optional arguments, as described
+     *                            {@link #altMetafactory(MethodHandles.Lookup, String, functionalInterface, Object...)} above}
+     * @return a function object which is an instance of a class which implements the interface named
+     *         by {@code functionalInterface}
+     * @throws LambdaConversionException If any of the linkage invariants
+     *                                   described {@link LambdaMetafactory above}
+     *                                   are violated
+     */
+    public static Object altMetafactory(MethodHandles.Lookup caller,
+                                          String invokedName,
+                                          Class<?> functionalInterface,
+                                          Object... args)
+            throws LambdaConversionException {
+        try {
+            return altMetafactory(caller, invokedName,
+                    MethodType.methodType(functionalInterface), args).getTarget().invoke();
+        }
+        catch (LambdaConversionException | LinkageError e) {
+            throw e;
+        }
+        catch (Throwable e) {
+            throw new LambdaConversionException("Exception invoking lambda metafactory", e);
+        }
+    }
 }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java	Thu Jan 24 18:31:17 2019 -0500
@@ -188,7 +188,8 @@
         IMPORT_ON_DEMAND_OBSERVABLE_PACKAGES(JDK1_2, JDK8),
         SWITCH_MULTIPLE_CASE_LABELS(JDK13, Fragments.FeatureMultipleCaseLabels, DiagKind.PLURAL),
         SWITCH_RULE(JDK13, Fragments.FeatureSwitchRules, DiagKind.PLURAL),
-        SWITCH_EXPRESSION(JDK13, Fragments.FeatureSwitchExpressions, DiagKind.PLURAL);
+        SWITCH_EXPRESSION(JDK13, Fragments.FeatureSwitchExpressions, DiagKind.PLURAL),
+        CONDY_FOR_LAMBDA(JDK13);
 
         enum DiagKind {
             NORMAL,
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Jan 24 18:31:17 2019 -0500
@@ -1548,6 +1548,10 @@
             return name.toString();
         }
 
+        public boolean isDynamic() {
+            return false;
+        }
+
         public Symbol asMemberOf(Type site, Types types) {
             return new VarSymbol(flags_field, name, types.memberType(site, this), owner);
         }
@@ -1989,7 +1993,7 @@
     public static class DynamicMethodSymbol extends MethodSymbol {
 
         public Object[] staticArgs;
-        public Symbol bsm;
+        public MethodSymbol bsm;
         public int bsmKind;
 
         public DynamicMethodSymbol(Name name, Symbol owner, int bsmKind, MethodSymbol bsm, Type type, Object[] staticArgs) {
@@ -2005,6 +2009,26 @@
         }
     }
 
+    /** A class for condy.
+     */
+    public static class DynamicVarSymbol extends VarSymbol {
+        public Object[] staticArgs;
+        public MethodSymbol bsm;
+        public int bsmKind;
+
+        public DynamicVarSymbol(Name name, Symbol owner, int bsmKind, MethodSymbol bsm, Type type, Object[] staticArgs) {
+            super(0, name, type, owner);
+            this.bsm = bsm;
+            this.bsmKind = bsmKind;
+            this.staticArgs = staticArgs;
+        }
+
+        @Override
+        public boolean isDynamic() {
+            return true;
+        }
+    }
+
     /** A class for predefined operators.
      */
     public static class OperatorSymbol extends MethodSymbol {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Thu Jan 24 18:31:17 2019 -0500
@@ -35,9 +35,12 @@
 import com.sun.tools.javac.tree.TreeTranslator;
 import com.sun.tools.javac.code.Attribute;
 import com.sun.tools.javac.code.Scope.WriteableScope;
+import com.sun.tools.javac.code.Source;
+import com.sun.tools.javac.code.Source.Feature;
 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.DynamicVarSymbol;
 import com.sun.tools.javac.code.Symbol.MethodSymbol;
 import com.sun.tools.javac.code.Symbol.TypeSymbol;
 import com.sun.tools.javac.code.Symbol.VarSymbol;
@@ -170,6 +173,12 @@
                 || options.isSet(Option.G_CUSTOM, "vars");
         verboseDeduplication = options.isSet("debug.dumpLambdaToMethodDeduplication");
         deduplicateLambdas = options.getBoolean("deduplicateLambdas", true);
+        Source source = Source.instance(context);
+        // format: -XDforNonCapturingLambda=generateCondy, which is the default, or -XDforNonCapturingLambda=generateIndy
+        String condyOp = options.get("forNonCapturingLambda");
+        condyForLambda = (condyOp != null ?
+                condyOp.equals("generateCondy") :
+                Feature.CONDY_FOR_LAMBDA.allowedInSource(source));
     }
     // </editor-fold>
 
@@ -791,11 +800,11 @@
                     "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
                     "getImplClass", implClass),
                     "getImplMethodSignature", implMethodSignature),
-                make.Return(makeIndyCall(
+                make.Return(makeDynamicCall(
                     pos,
                     syms.lambdaMetafactory,
                     names.altMetafactory,
-                    staticArgs, indyType, serArgs.toList(), samSym.name)),
+                    staticArgs, targetType, indyType, serArgs.toList(), samSym.name)),
                 null);
         ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
         if (stmts == null) {
@@ -1177,8 +1186,7 @@
                 }
             }
         }
-
-        return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args, samSym.name);
+        return makeDynamicCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, tree.type, indyType, indy_args, samSym.name);
     }
 
     /**
@@ -1220,6 +1228,52 @@
             make.at(prevPos);
         }
     }
+
+    private JCExpression makeDynamicCall(DiagnosticPosition pos, Type site, Name bsmName,
+                                         List<Object> staticArgs, Type interfaceType, MethodType indyType, List<JCExpression> indyArgs,
+                                         Name methName) {
+        return condyForLambda &&
+                !context.needsAltMetafactory() &&
+                indyArgs.isEmpty() ?
+                makeCondy(pos, site, bsmName, staticArgs, interfaceType, methName) :
+                makeIndyCall(pos, site, bsmName, staticArgs, indyType, indyArgs, methName);
+    }
+
+    /* this extra flag should be temporary and used as long as it's not possible to do the build
+     * due to the lack of support for condy in the current version of ASM present in the build
+     */
+    private final boolean condyForLambda;
+
+    private JCExpression makeCondy(DiagnosticPosition pos, Type site, Name bsmName,
+                                   List<Object> staticArgs, Type interfaceType, Name methName) {
+        int prevPos = make.pos;
+        try {
+            make.at(pos);
+            List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
+                    syms.stringType,
+                    syms.classType).appendList(bsmStaticArgToTypes(staticArgs));
+
+            Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
+                    bsmName, bsm_staticArgs, List.nil());
+
+            Symbol.DynamicVarSymbol dynSym = new Symbol.DynamicVarSymbol(methName,
+                    syms.noSymbol,
+                    bsm.isStatic() ?
+                            ClassFile.REF_invokeStatic :
+                            ClassFile.REF_invokeVirtual,
+                    (MethodSymbol)bsm,
+                    interfaceType,
+                    staticArgs.toArray());
+
+            JCIdent ident = make.Ident(dynSym);
+            ident.type = interfaceType;
+
+            return ident;
+        } finally {
+            make.at(prevPos);
+        }
+    }
+
     //where
     private List<Type> bsmStaticArgToTypes(List<Object> args) {
         ListBuffer<Type> argtypes = new ListBuffer<>();
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java	Thu Jan 24 18:31:17 2019 -0500
@@ -156,6 +156,9 @@
     /** The name table. */
     private final Names names;
 
+    /** The symbol table. */
+    private final Symtab syms;
+
     /** Access to files. */
     private final JavaFileManager fileManager;
 
@@ -184,6 +187,7 @@
 
         log = Log.instance(context);
         names = Names.instance(context);
+        syms = Symtab.instance(context);
         options = Options.instance(context);
         preview = Preview.instance(context);
         target = Target.instance(context);
@@ -407,31 +411,33 @@
                     //invokedynamic
                     DynamicMethodSymbol dynSym = (DynamicMethodSymbol)m;
                     MethodHandle handle = new MethodHandle(dynSym.bsmKind, dynSym.bsm, types);
-                    DynamicMethod.BootstrapMethodsKey key = new DynamicMethod.BootstrapMethodsKey(dynSym, types);
-
-                    // Figure out the index for existing BSM; create a new BSM if no key
-                    DynamicMethod.BootstrapMethodsValue val = bootstrapMethods.get(key);
-                    if (val == null) {
-                        int index = bootstrapMethods.size();
-                        val = new DynamicMethod.BootstrapMethodsValue(handle, index);
-                        bootstrapMethods.put(key, val);
-                    }
-
-                    //init cp entries
-                    pool.put(names.BootstrapMethods);
-                    pool.put(handle);
-                    for (Object staticArg : dynSym.staticArgs) {
-                        pool.put(staticArg);
-                    }
+                    DynamicMethod.BootstrapMethodsValue val = writeDynSymbol(dynSym, handle);
                     poolbuf.appendByte(CONSTANT_InvokeDynamic);
                     poolbuf.appendChar(val.index);
                     poolbuf.appendChar(pool.put(nameType(dynSym)));
                 }
             } else if (value instanceof VarSymbol) {
                 VarSymbol v = (VarSymbol)value;
-                poolbuf.appendByte(CONSTANT_Fieldref);
-                poolbuf.appendChar(pool.put(v.owner));
-                poolbuf.appendChar(pool.put(nameType(v)));
+                if (!v.isDynamic()) {
+                    poolbuf.appendByte(CONSTANT_Fieldref);
+                    poolbuf.appendChar(pool.put(v.owner));
+                    poolbuf.appendChar(pool.put(nameType(v)));
+                } else {
+                    DynamicVarSymbol dynVarSym = (DynamicVarSymbol)v;
+                    MethodHandle handle = new MethodHandle(dynVarSym.bsmKind, dynVarSym.bsm, types);
+                    DynamicMethodSymbol dynSym = new DynamicMethodSymbol(
+                            handle.refSym.name,
+                            syms.noSymbol,
+                            handle.refKind,
+                            (MethodSymbol)handle.refSym,
+                            handle.refSym.type,
+                            dynVarSym.staticArgs);
+                    DynamicMethod.BootstrapMethodsValue val = writeDynSymbol(dynSym, handle);
+                    poolbuf.appendByte(CONSTANT_Dynamic);
+                    poolbuf.appendChar(val.index);
+                    NameAndType nt = new NameAndType(dynVarSym.name, dynVarSym.type, types);
+                    poolbuf.appendChar(pool.put(nt));
+                }
             } else if (value instanceof Name) {
                 poolbuf.appendByte(CONSTANT_Utf8);
                 byte[] bs = ((Name)value).toUtf();
@@ -504,6 +510,26 @@
         putChar(poolbuf, poolCountIdx, pool.pp);
     }
 
+    DynamicMethod.BootstrapMethodsValue writeDynSymbol(DynamicMethodSymbol dynSym, MethodHandle handle) {
+        DynamicMethod.BootstrapMethodsKey key = new DynamicMethod.BootstrapMethodsKey(dynSym, types);
+
+        // Figure out the index for existing BSM; create a new BSM if no key
+        DynamicMethod.BootstrapMethodsValue val = bootstrapMethods.get(key);
+        if (val == null) {
+            int index = bootstrapMethods.size();
+            val = new DynamicMethod.BootstrapMethodsValue(handle, index);
+            bootstrapMethods.put(key, val);
+        }
+
+        //init cp entries
+        pool.put(names.BootstrapMethods);
+        pool.put(handle);
+        for (Object staticArg : dynSym.staticArgs) {
+            pool.put(staticArg);
+        }
+        return val;
+    }
+
     /** Given a symbol, return its name-and-type.
      */
     NameAndType nameType(Symbol sym) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Code.java	Thu Jan 24 18:31:17 2019 -0500
@@ -922,6 +922,7 @@
         if (o instanceof ClassSymbol) return syms.classType;
         if (o instanceof Pool.MethodHandle) return syms.methodHandleType;
         if (o instanceof UniqueType) return typeForPool(((UniqueType)o).type);
+        if (o instanceof Pool.DynamicVariable) return ((Pool.DynamicVariable)o).type;
         if (o instanceof Type) {
             Type ty = (Type) o;
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java	Thu Jan 24 18:31:17 2019 -0500
@@ -847,7 +847,12 @@
                 // Short circuit any expressions which are constants
                 tree.accept(classReferenceVisitor);
                 checkStringConstant(tree.pos(), tree.type.constValue());
-                result = items.makeImmediateItem(tree.type, tree.type.constValue());
+                Symbol sym = TreeInfo.symbol(tree);
+                if (sym != null && isConstantDynamic(sym)) {
+                    result = items.makeDynamicItem(sym);
+                } else {
+                    result = items.makeImmediateItem(tree.type, tree.type.constValue());
+                }
             } else {
                 this.pt = pt;
                 tree.accept(this);
@@ -2195,10 +2200,13 @@
                 res = items.makeMemberItem(sym, true);
             }
             result = res;
+        } else if (isInvokeDynamic(sym) || isConstantDynamic(sym)) {
+            if (isConstantDynamic(sym)) {
+                setTypeAnnotationPositions(tree.pos);
+            }
+            result = items.makeDynamicItem(sym);
         } else if (sym.kind == VAR && (sym.owner.kind == MTH || sym.owner.kind == VAR)) {
             result = items.makeLocalItem((VarSymbol)sym);
-        } else if (isInvokeDynamic(sym)) {
-            result = items.makeDynamicItem(sym);
         } else if ((sym.flags() & STATIC) != 0) {
             if (!isAccessSuper(env.enclMethod))
                 sym = binaryQualifier(sym, env.enclClass.type);
@@ -2217,6 +2225,12 @@
         return !useVirtual && ((sym.flags() & PRIVATE) != 0);
     }
 
+    public boolean isConstantDynamic(Symbol sym) {
+        return sym.kind == VAR &&
+                sym instanceof DynamicVarSymbol &&
+                ((DynamicVarSymbol)sym).isDynamic();
+    }
+
     public void visitSelect(JCFieldAccess tree) {
         Symbol sym = tree.sym;
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Items.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Items.java	Thu Jan 24 18:31:17 2019 -0500
@@ -29,6 +29,7 @@
 import com.sun.tools.javac.code.Symbol.*;
 import com.sun.tools.javac.code.Type.*;
 import com.sun.tools.javac.jvm.Code.*;
+import com.sun.tools.javac.jvm.Pool.DynamicVariable;
 import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.util.Assert;
 
@@ -163,6 +164,13 @@
         return new ImmediateItem(type, value);
     }
 
+    /** Make an item representing a condy.
+     *  @param value    The condy value.
+     */
+    Item makeCondyItem(DynamicVariable value) {
+        return new CondyItem(value);
+    }
+
     /** Make an item representing an assignment expression.
      *  @param lhs      The item representing the assignment's left hand side.
      */
@@ -472,8 +480,11 @@
         }
 
         Item load() {
-            assert false;
-            return null;
+            Assert.check(member.kind == Kinds.Kind.VAR);
+            Type type = member.erasure(types);
+            int rescode = Code.typecode(type);
+            code.emitLdc(pool.put(member));
+            return stackItem[rescode];
         }
 
         void store() {
@@ -481,7 +492,7 @@
         }
 
         Item invoke() {
-            // assert target.hasNativeInvokeDynamic();
+            Assert.check(member.kind == Kinds.Kind.MTH);
             MethodType mtype = (MethodType)member.erasure(types);
             int rescode = Code.typecode(mtype.restype);
             code.emitInvokedynamic(pool.put(member), mtype);
@@ -493,6 +504,33 @@
         }
     }
 
+    /** An item representing a condy
+     */
+    class CondyItem extends Item {
+        DynamicVariable value;
+
+        CondyItem(DynamicVariable value) {
+            super(Code.typecode(value.type));
+            this.value = value;
+        }
+
+        @Override
+        public String toString() {
+            return "condy(" + value + ")";
+        }
+
+        @Override
+        Item load() {
+            int idx = pool.put(value);
+            if (typecode == LONGcode || typecode == DOUBLEcode) {
+                code.emitop2(ldc2w, idx);
+            } else {
+                code.emitLdc(idx);
+            }
+            return stackItem[typecode];
+        }
+    }
+
     /** An item representing an instance variable or method.
      */
     class MemberItem extends Item {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Pool.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Pool.java	Thu Jan 24 18:31:17 2019 -0500
@@ -27,6 +27,7 @@
 
 import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.code.Symtab;
 import com.sun.tools.javac.code.TypeTag;
 import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.code.Types;
@@ -128,6 +129,8 @@
     Object makePoolValue(Object o) {
         if (o instanceof DynamicMethodSymbol) {
             return new DynamicMethod((DynamicMethodSymbol)o, types);
+        } else if (o instanceof DynamicVarSymbol) {
+            return new Pool.DynamicVariable((DynamicVarSymbol) o, types);
         } else if (o instanceof MethodSymbol) {
             return new Method((MethodSymbol)o, types);
         } else if (o instanceof VarSymbol) {
@@ -182,10 +185,12 @@
 
     public static class DynamicMethod extends Method {
         public Object[] uniqueStaticArgs;
+        Method internalBSM;
 
         public DynamicMethod(DynamicMethodSymbol m, Types types) {
             super(m, types);
             uniqueStaticArgs = getUniqueTypeArray(m.staticArgs, types);
+            internalBSM = new Method(m.bsm, types);
         }
 
         @Override @DefinedBy(Api.LANGUAGE_MODEL)
@@ -198,7 +203,7 @@
             if (!(any instanceof DynamicMethod)) return false;
             DynamicMethodSymbol dm1 = (DynamicMethodSymbol)other;
             DynamicMethodSymbol dm2 = (DynamicMethodSymbol)((DynamicMethod)any).other;
-            return dm1.bsm == dm2.bsm &&
+            return internalBSM.equals(((DynamicMethod)any).internalBSM) &&
                         dm1.bsmKind == dm2.bsmKind &&
                         Arrays.equals(uniqueStaticArgs,
                             ((DynamicMethod)any).uniqueStaticArgs);
@@ -213,7 +218,7 @@
             int hash = includeDynamicArgs ? super.hashCode() : 0;
             DynamicMethodSymbol dm = (DynamicMethodSymbol)other;
             hash += dm.bsmKind * 7 +
-                    dm.bsm.hashCode() * 11;
+                    internalBSM.hashCode() * 11;
             for (int i = 0; i < dm.staticArgs.length; i++) {
                 hash += (uniqueStaticArgs[i].hashCode() * 23);
             }
@@ -289,6 +294,80 @@
         }
     }
 
+    /**
+     * Pool entry associated with dynamic constants.
+     */
+    public static class DynamicVariable extends Variable {
+        public MethodHandle bsm;
+        private Object[] uniqueStaticArgs;
+        Types types;
+
+        public DynamicVariable(Name name, MethodHandle bsm, Object[] args, Types types, Symtab syms) {
+            this(name, bsm, bsm.refSym.type.asMethodType().restype, args, types, syms);
+        }
+
+        public DynamicVariable(Name name, MethodHandle bsm, Type type, Object[] args, Types types, Symtab syms) {
+            this(new DynamicVarSymbol(name,
+                    syms.noSymbol,
+                    bsm.refKind,
+                    (MethodSymbol)bsm.refSym,
+                    type,
+                    args), types, bsm);
+        }
+
+        public DynamicVariable(DynamicVarSymbol dynField, Types types) {
+            this(dynField, types, null);
+        }
+
+        private DynamicVariable(DynamicVarSymbol dynField, Types types, MethodHandle bsm) {
+            super(dynField, types);
+            this.bsm = bsm != null ?
+                    bsm :
+                    new MethodHandle(dynField.bsmKind, dynField.bsm, types);
+            this.types = types;
+            uniqueStaticArgs = getUniqueTypeArray(staticArgs(), types);
+        }
+
+        private Object[] getUniqueTypeArray(Object[] objects, Types types) {
+            Object[] result = new Object[objects.length];
+            for (int i = 0; i < objects.length; i++) {
+                if (objects[i] instanceof Type) {
+                    result[i] = new UniqueType((Type)objects[i], types);
+                } else {
+                    result[i] = objects[i];
+                }
+            }
+            return result;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = bsm.hashCode() * 67 + other.name.hashCode() + type.hashCode() * 13 + uniqueType.hashCode();
+            for (Object uniqueStaticArg : uniqueStaticArgs) {
+                hash += (uniqueStaticArg.hashCode() * 23);
+            }
+            return hash;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof DynamicVariable) {
+                DynamicVariable that = (DynamicVariable)obj;
+                return that.bsm.equals(bsm) &&
+                        types.isSameType(that.type, type) &&
+                        that.other.name.equals(other.name) &&
+                        Arrays.equals(uniqueStaticArgs, that.uniqueStaticArgs) &&
+                        that.uniqueType.equals(uniqueType);
+            } else {
+                return false;
+            }
+        }
+
+        public Object[] staticArgs() {
+            return ((DynamicVarSymbol)other).staticArgs;
+        }
+    }
+
     public static class MethodHandle {
 
         /** Reference kind - see ClassFile */
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java	Thu Jan 24 22:08:03 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java	Thu Jan 24 18:31:17 2019 -0500
@@ -168,5 +168,4 @@
     public boolean hasVirtualPrivateInvoke() {
         return compareTo(JDK1_11) >= 0;
     }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/condy/CheckCondyGeneratedForLambdaTest.java	Thu Jan 24 18:31:17 2019 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017, 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
+ * @summary check that an LDC + condy is generated for a non-capturing lambda
+ * @library /tools/lib
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.util
+ *          jdk.jdeps/com.sun.tools.javap
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run main CheckCondyGeneratedForLambdaTest
+ */
+
+import java.io.File;
+import java.nio.file.Paths;
+
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.ConstantPool.*;
+import com.sun.tools.classfile.Method;
+import com.sun.tools.classfile.Opcode;
+import com.sun.tools.javac.util.Assert;
+
+import toolbox.JavacTask;
+import toolbox.ToolBox;
+
+public class CheckCondyGeneratedForLambdaTest {
+
+    static final String testSource =
+        "import java.util.stream.*;\n" +
+
+        "public class CondyForLambdaSmokeTest {\n" +
+        "    void lookForThisMethod() {\n" +
+        "        IntStream.of(1,2,3).reduce((a,b) -> a+b);\n" +
+        "    }\n" +
+        "}";
+
+    static final String methodToLookFor = "lookForThisMethod";
+    static final int LDCByteCodePos = 18;
+
+    public static void main(String[] args) throws Exception {
+        new CheckCondyGeneratedForLambdaTest().run();
+    }
+
+    ToolBox tb = new ToolBox();
+
+    void run() throws Exception {
+        compileTestClass();
+        checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
+                "CondyForLambdaSmokeTest.class").toUri()), methodToLookFor);
+    }
+
+    void compileTestClass() throws Exception {
+        new JavacTask(tb)
+                .sources(testSource)
+                .run();
+    }
+
+    void checkClassFile(final File cfile, String methodToFind) throws Exception {
+        ClassFile classFile = ClassFile.read(cfile);
+        boolean methodFound = false;
+        for (Method method : classFile.methods) {
+            if (method.getName(classFile.constant_pool).equals(methodToFind)) {
+                methodFound = true;
+                Code_attribute code = (Code_attribute) method.attributes.get("Code");
+                int cpIndex = code.getUnsignedByte(LDCByteCodePos + 1);
+                Assert.check(code.getUnsignedByte(LDCByteCodePos) == Opcode.LDC.opcode, "ldc was expected");
+                CPInfo cpInfo = classFile.constant_pool.get(cpIndex);
+                Assert.check(cpInfo instanceof CONSTANT_Dynamic_info, "condy argument to ldc was expected");
+            }
+        }
+        Assert.check(methodFound, "The seek method was not found");
+    }
+
+    void error(String msg) {
+        throw new AssertionError(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/condy/CheckForCondyDuplicatesTest.java	Thu Jan 24 18:31:17 2019 -0500
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2018, 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
+ * @summary check that javac is not generating duplicate condys
+ * @library /tools/lib
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.util
+ *          jdk.jdeps/com.sun.tools.javap
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @ignore
+ * @run main CheckForCondyDuplicatesTest
+ */
+
+import java.io.File;
+import java.nio.file.Paths;
+import java.lang.constant.*;
+
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPool.*;
+import com.sun.tools.javac.util.Assert;
+
+import toolbox.JavacTask;
+import toolbox.ToolBox;
+
+public class CheckForCondyDuplicatesTest {
+
+    static final String testSource1 =
+        "import java.lang.constant.*;\n" +
+        "import java.lang.invoke.*;\n" +
+
+        "public class Test1 {\n" +
+        "    void m() {\n" +
+        "        Object o1 = Intrinsics.ldc(ConstantDescs.NULL);\n" +
+        "        Object o2 = Intrinsics.ldc(ConstantDescs.NULL);\n" +
+        "    }\n" +
+        "}";
+
+    static final String testSource2 =
+        "class Test2 {\n" +
+        "   Runnable r = Test2::foo;\n" +
+        "   Runnable k = Test2::foo;\n" +
+        "   static void foo() {}\n" +
+        "}";
+
+    public static void main(String[] args) throws Exception {
+        new CheckForCondyDuplicatesTest().run();
+    }
+
+    ToolBox tb = new ToolBox();
+
+    void run() throws Exception {
+        compileTestClass(testSource1);
+        checkClassFile(new File(Paths.get(System.getProperty("user.dir"), "Test1.class").toUri()));
+
+        compileTestClass(testSource2);
+        checkClassFile(new File(Paths.get(System.getProperty("user.dir"), "Test2.class").toUri()));
+    }
+
+    void compileTestClass(String source) throws Exception {
+        new JavacTask(tb)
+                .sources(source)
+                .run();
+    }
+
+    void checkClassFile(final File cfile) throws Exception {
+        ClassFile classFile = ClassFile.read(cfile);
+        int numberOfCondys = 0;
+        for (CPInfo cpInfo : classFile.constant_pool.entries()) {
+            if (cpInfo instanceof CONSTANT_Dynamic_info) {
+                numberOfCondys++;
+            }
+        }
+        Assert.check(numberOfCondys > 0, "there should be at least one condy in the class file");
+        Assert.check(numberOfCondys == 1, "the CP has duplicate condys");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/condy/LambdaSerializationTest.java	Thu Jan 24 18:31:17 2019 -0500
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2018, 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
+ * @summary check that serializable lambdas work independently of the serialization approach
+ * @library /tools/lib
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.util
+ *          jdk.jdeps/com.sun.tools.javap
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run main LambdaSerializationTest
+ */
+
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import com.sun.tools.javac.util.Assert;
+import toolbox.JavacTask;
+import toolbox.ToolBox;
+
+public class LambdaSerializationTest {
+    private static final String source =
+            "import java.io.Serializable;\n" +
+            "import java.util.function.*;\n" +
+            "public class Test {\n" +
+            "    public static String foo() {\n" +
+            "        Function<String, String> f = (Function<String, String> & Serializable)(s) -> s;\n" +
+            "        return f.apply(\"From serializable lambda\");\n" +
+            "    }\n" +
+            "}";
+
+    static final String testOut = System.getProperty("user.dir");
+
+    public static void main(String... args) throws Throwable {
+        new LambdaSerializationTest().run();
+    }
+
+    ToolBox tb = new ToolBox();
+
+    void run() throws Throwable {
+        compileTestClass(false);
+        String res1 = loadAndInvoke();
+        compileTestClass(true);
+        String res2 = loadAndInvoke();
+        Assert.check(res1.equals(res2));
+        Assert.check(res1.equals("From serializable lambda"));
+    }
+
+    void compileTestClass(boolean generateCondy) throws Throwable {
+        String option = generateCondy ? "-XDforNonCapturingLambda=generateCondy" : "-XDforNonCapturingLambda=generateIndy";
+        new JavacTask(tb)
+                .options(option)
+                .sources(source)
+                .run();
+    }
+
+    String loadAndInvoke() throws Throwable {
+        Path path = Paths.get(testOut);
+        System.out.println(path);
+        ClassLoader cl = new URLClassLoader(new URL[] { path.toUri().toURL() });
+        Class<?> testClass = cl.loadClass("Test");
+        Method theMethod = testClass.getDeclaredMethod("foo", new Class<?>[0]);
+        return (String)theMethod.invoke(null, new Object[0]);
+    }
+}