internal virtual LambdaForm CollectArgumentsForm(int pos, MethodType collectorType)
        {
            int  collectorArity = collectorType.ParameterCount();
            bool dropResult     = (collectorType.ReturnType() == typeof(void));

            if (collectorArity == 1 && !dropResult)
            {
                return(FilterArgumentForm(pos, basicType(collectorType.ParameterType(0))));
            }
            BasicType[]    newTypes = BasicType.basicTypes(collectorType.ParameterList());
            Transform.Kind kind     = (dropResult ? Transform.Kind.COLLECT_ARGS_TO_VOID : Transform.Kind.COLLECT_ARGS);
            if (dropResult && collectorArity == 0)             // pure side effect
            {
                pos = 1;
            }
            Transform  key  = Transform.Of(kind, pos, collectorArity, BasicType.basicTypesOrd(newTypes));
            LambdaForm form = GetInCache(key);

            if (form != null)
            {
                assert(form.Arity_Renamed == LambdaForm.Arity_Renamed - (dropResult ? 0 : 1) + collectorArity);
                return(form);
            }
            form = MakeArgumentCombinationForm(pos, collectorType, false, dropResult);
            return(PutInCache(key, form));
        }
        /// <summary>
        /// Facilitates the creation of simple "function objects" that implement one
        /// or more interfaces by delegation to a provided <seealso cref="MethodHandle"/>,
        /// after appropriate type adaptation and partial evaluation of arguments.
        /// Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
        /// call sites, to support the <em>lambda expression</em> and <em>method
        /// reference expression</em> features of the Java Programming Language.
        ///
        /// <para>This is the general, more flexible metafactory; a streamlined version
        /// is provided by {@link #metafactory(java.lang.invoke.MethodHandles.Lookup,
        /// String, MethodType, MethodType, MethodHandle, MethodType)}.
        /// A general description of the behavior of this method is provided
        /// <seealso cref="LambdaMetafactory above"/>.
        ///
        /// </para>
        /// <para>The argument list for this method includes three fixed parameters,
        /// corresponding to the parameters automatically stacked by the VM for the
        /// bootstrap method in an {@code invokedynamic} invocation, and an {@code Object[]}
        /// parameter that contains additional parameters.  The declared argument
        /// list for this method is:
        ///
        /// <pre>{@code
        ///  CallSite altMetafactory(MethodHandles.Lookup caller,
        ///                          String invokedName,
        ///                          MethodType invokedType,
        ///                          Object... args)
        /// }</pre>
        ///
        /// </para>
        /// <para>but it behaves as if the argument list is as follows:
        ///
        /// <pre>{@code
        ///  CallSite altMetafactory(MethodHandles.Lookup caller,
        ///                          String invokedName,
        ///                          MethodType invokedType,
        ///                          MethodType samMethodType,
        ///                          MethodHandle implMethod,
        ///                          MethodType instantiatedMethodType,
        ///                          int flags,
        ///                          int markerInterfaceCount,  // IF flags has MARKERS set
        ///                          Class... markerInterfaces, // IF flags has MARKERS set
        ///                          int bridgeCount,           // IF flags has BRIDGES set
        ///                          MethodType... bridges      // IF flags has BRIDGES set
        ///                          )
        /// }</pre>
        ///
        /// </para>
        /// <para>Arguments that appear in the argument list for
        /// <seealso cref="#metafactory(MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)"/>
        /// have the same specification as in that method.  The additional arguments
        /// are interpreted as follows:
        /// <ul>
        ///     <li>{@code flags} indicates additional options; this is a bitwise
        ///     OR of desired flags.  Defined flags are <seealso cref="#FLAG_BRIDGES"/>,
        ///     <seealso cref="#FLAG_MARKERS"/>, and <seealso cref="#FLAG_SERIALIZABLE"/>.</li>
        ///     <li>{@code markerInterfaceCount} is the number of additional interfaces
        ///     the function object should implement, and is present if and only if the
        ///     {@code FLAG_MARKERS} flag is set.</li>
        ///     <li>{@code markerInterfaces} is a variable-length list of additional
        ///     interfaces to implement, whose length equals {@code markerInterfaceCount},
        ///     and is present if and only if the {@code FLAG_MARKERS} flag is set.</li>
        ///     <li>{@code bridgeCount} is the number of additional method signatures
        ///     the function object should implement, and is present if and only if
        ///     the {@code FLAG_BRIDGES} flag is set.</li>
        ///     <li>{@code bridges} is a variable-length list of additional
        ///     methods signatures to implement, whose length equals {@code bridgeCount},
        ///     and is present if and only if the {@code FLAG_BRIDGES} flag is set.</li>
        /// </ul>
        ///
        /// </para>
        /// <para>Each class named by {@code markerInterfaces} is subject to the same
        /// restrictions as {@code Rd}, the return type of {@code invokedType},
        /// as described <seealso cref="LambdaMetafactory above"/>.  Each {@code MethodType}
        /// named by {@code bridges} is subject to the same restrictions as
        /// {@code samMethodType}, as described <seealso cref="LambdaMetafactory above"/>.
        ///
        /// </para>
        /// <para>When FLAG_SERIALIZABLE is set in {@code flags}, the function objects
        /// will implement {@code Serializable}, and will have a {@code writeReplace}
        /// method that returns an appropriate <seealso cref="SerializedLambda"/>.  The
        /// {@code caller} class must have an appropriate {@code $deserializeLambda$}
        /// method, as described in <seealso cref="SerializedLambda"/>.
        ///
        /// </para>
        /// <para>When the target of the {@code CallSite} returned from this method is
        /// invoked, the resulting function objects are instances of a class with
        /// the following properties:
        /// <ul>
        ///     <li>The class implements the interface named by the return type
        ///     of {@code invokedType} and any interfaces named by {@code markerInterfaces}</li>
        ///     <li>The class declares methods with the name given by {@code invokedName},
        ///     and the signature given by {@code samMethodType} and additional signatures
        ///     given by {@code bridges}</li>
        ///     <li>The class may override methods from {@code Object}, and may
        ///     implement methods related to serialization.</li>
        /// </ul>
        ///
        /// </para>
        /// </summary>
        /// <param name="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>
        /// <param name="invokedName"> The name of the method to 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. </param>
        /// <param name="invokedType"> The expected signature of the {@code CallSite}.  The
        ///                    parameter types represent the types of capture variables;
        ///                    the return type is the interface to 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>
        /// <param name="args">       An {@code Object[]} array containing the required
        ///                    arguments {@code samMethodType}, {@code implMethod},
        ///                    {@code instantiatedMethodType}, {@code flags}, and any
        ///                    optional arguments, as described
        ///                    <seealso cref="#altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)"/> above} </param>
        /// <returns> a CallSite whose target can be used to perform capture, generating
        ///         instances of the interface named by {@code invokedType} </returns>
        /// <exception cref="LambdaConversionException"> If any of the linkage invariants
        ///                                   described <seealso cref="LambdaMetafactory above"/>
        ///                                   are violated </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public static CallSite altMetafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, Object... args) throws LambdaConversionException
        public static CallSite AltMetafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, params Object[] args)
        {
            MethodType   samMethodType          = (MethodType)args[0];
            MethodHandle implMethod             = (MethodHandle)args[1];
            MethodType   instantiatedMethodType = (MethodType)args[2];
            int          flags = (Integer)args[3];

            Class[]      markerInterfaces;
            MethodType[] bridges;
            int          argIndex = 4;

            if ((flags & FLAG_MARKERS) != 0)
            {
                int markerCount = (Integer)args[argIndex++];
                markerInterfaces = new Class[markerCount];
                System.Array.Copy(args, argIndex, markerInterfaces, 0, markerCount);
                argIndex += markerCount;
            }
            else
            {
                markerInterfaces = EMPTY_CLASS_ARRAY;
            }
            if ((flags & FLAG_BRIDGES) != 0)
            {
                int bridgeCount = (Integer)args[argIndex++];
                bridges = new MethodType[bridgeCount];
                System.Array.Copy(args, argIndex, bridges, 0, bridgeCount);
                argIndex += bridgeCount;
            }
            else
            {
                bridges = EMPTY_MT_ARRAY;
            }

            bool isSerializable = ((flags & FLAG_SERIALIZABLE) != 0);

            if (isSerializable)
            {
                bool foundSerializableSupertype = invokedType.ReturnType().IsSubclassOf(typeof(Serializable));
                foreach (Class c in markerInterfaces)
                {
                    foundSerializableSupertype |= c.IsSubclassOf(typeof(Serializable));
                }
                if (!foundSerializableSupertype)
                {
                    markerInterfaces = Arrays.CopyOf(markerInterfaces, markerInterfaces.Length + 1);
                    markerInterfaces[markerInterfaces.Length - 1] = typeof(Serializable);
                }
            }

            AbstractValidatingLambdaMetafactory mf = new InnerClassLambdaMetafactory(caller, invokedType, invokedName, samMethodType, implMethod, instantiatedMethodType, isSerializable, markerInterfaces, bridges);

            mf.ValidateMetafactoryArgs();
            return(mf.BuildCallSite());
        }
        internal readonly MethodType[] AdditionalBridges;    // Signatures of additional methods to bridge


        /// <summary>
        /// Meta-factory constructor.
        /// </summary>
        /// <param name="caller"> Stacked automatically by VM; represents a lookup context
        ///               with the accessibility privileges of the caller. </param>
        /// <param name="invokedType"> Stacked automatically by VM; the signature of the
        ///                    invoked method, which includes the expected static
        ///                    type of the returned lambda object, and the static
        ///                    types of the captured arguments for the lambda.  In
        ///                    the event that the implementation method is an
        ///                    instance method, the first argument in the invocation
        ///                    signature will correspond to the receiver. </param>
        /// <param name="samMethodName"> Name of the method in the functional interface to
        ///                      which the lambda or method reference is being
        ///                      converted, represented as a String. </param>
        /// <param name="samMethodType"> Type of the method in the functional interface to
        ///                      which the lambda or method reference is being
        ///                      converted, represented as a MethodType. </param>
        /// <param name="implMethod"> The implementation method which should be called
        ///                   (with suitable adaptation of argument types, return
        ///                   types, and adjustment for captured arguments) when
        ///                   methods of the resulting functional interface instance
        ///                   are invoked. </param>
        /// <param name="instantiatedMethodType"> The signature of the primary functional
        ///                               interface method after type variables are
        ///                               substituted with their instantiation from
        ///                               the capture site </param>
        /// <param name="isSerializable"> Should the lambda be made serializable?  If set,
        ///                       either the target type or one of the additional SAM
        ///                       types must extend {@code Serializable}. </param>
        /// <param name="markerInterfaces"> Additional interfaces which the lambda object
        ///                       should implement. </param>
        /// <param name="additionalBridges"> Method types for additional signatures to be
        ///                          bridged to the implementation method </param>
        /// <exception cref="LambdaConversionException"> If any of the meta-factory protocol
        /// invariants are violated </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller, MethodType invokedType, String samMethodName, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType, boolean isSerializable, Class[] markerInterfaces, MethodType[] additionalBridges) throws LambdaConversionException
        internal AbstractValidatingLambdaMetafactory(MethodHandles.Lookup caller, MethodType invokedType, String samMethodName, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType, bool isSerializable, Class[] markerInterfaces, MethodType[] additionalBridges)
        {
            if ((caller.LookupModes() & MethodHandles.Lookup.PRIVATE) == 0)
            {
                throw new LambdaConversionException(string.Format("Invalid caller: {0}", caller.LookupClass().Name));
            }
            this.TargetClass = caller.LookupClass();
            this.InvokedType = invokedType;

            this.SamBase = invokedType.ReturnType();

            this.SamMethodName = samMethodName;
            this.SamMethodType = samMethodType;

            this.ImplMethod             = implMethod;
            this.ImplInfo               = caller.RevealDirect(implMethod);
            this.ImplKind               = ImplInfo.ReferenceKind;
            this.ImplIsInstanceMethod   = ImplKind == MethodHandleInfo.REF_invokeVirtual || ImplKind == MethodHandleInfo.REF_invokeSpecial || ImplKind == MethodHandleInfo.REF_invokeInterface;
            this.ImplDefiningClass      = ImplInfo.DeclaringClass;
            this.ImplMethodType         = ImplInfo.MethodType;
            this.InstantiatedMethodType = instantiatedMethodType;
            this.IsSerializable         = isSerializable;
            this.MarkerInterfaces       = markerInterfaces;
            this.AdditionalBridges      = additionalBridges;

            if (!SamBase.Interface)
            {
                throw new LambdaConversionException(string.Format("Functional interface {0} is not an interface", SamBase.Name));
            }

            foreach (Class c in markerInterfaces)
            {
                if (!c.Interface)
                {
                    throw new LambdaConversionException(string.Format("Marker interface {0} is not an interface", c.Name));
                }
            }
        }
        private LambdaForm MakeArgumentCombinationForm(int pos, MethodType combinerType, bool keepArguments, bool dropResult)
        {
            LambdaFormBuffer buf = Buffer();

            buf.StartEdit();
            int combinerArity = combinerType.ParameterCount();
            int resultArity   = (dropResult ? 0 : 1);

            assert(pos <= MethodType.MAX_JVM_ARITY);
            assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= LambdaForm.Arity_Renamed);
            assert(pos > 0);             // cannot filter the MH arg itself
            assert(combinerType == combinerType.BasicType());
            assert(combinerType.ReturnType() != typeof(void) || dropResult);

            BoundMethodHandle.SpeciesData oldData = OldSpeciesData();
            BoundMethodHandle.SpeciesData newData = NewSpeciesData(L_TYPE);

            // The newly created LF will run with a different BMH.
            // Switch over any pre-existing BMH field references to the new BMH class.
            Name oldBaseAddress = LambdaForm.Parameter(0);             // BMH holding the values

            buf.ReplaceFunctions(oldData.GetterFunctions(), newData.GetterFunctions(), oldBaseAddress);
            Name newBaseAddress = oldBaseAddress.withConstraint(newData);

            buf.RenameParameter(0, newBaseAddress);

            Name getCombiner = new Name(newData.GetterFunction(oldData.FieldCount()), newBaseAddress);

            Object[] combinerArgs = new Object[1 + combinerArity];
            combinerArgs[0] = getCombiner;
            Name[] newParams;
            if (keepArguments)
            {
                newParams = new Name[0];
                System.Array.Copy(LambdaForm.Names, pos + resultArity, combinerArgs, 1, combinerArity);
            }
            else
            {
                newParams = new Name[combinerArity];
                BasicType[] newTypes = basicTypes(combinerType.ParameterList());
                for (int i = 0; i < newTypes.Length; i++)
                {
                    newParams[i] = new Name(pos + i, newTypes[i]);
                }
                System.Array.Copy(newParams, 0, combinerArgs, 1, combinerArity);
            }
            Name callCombiner = new Name(combinerType, combinerArgs);

            // insert the two new expressions
            int exprPos = LambdaForm.Arity();

            buf.InsertExpression(exprPos + 0, getCombiner);
            buf.InsertExpression(exprPos + 1, callCombiner);

            // insert new arguments, if needed
            int argPos = pos + resultArity;             // skip result parameter

            foreach (Name newParam in newParams)
            {
                buf.InsertParameter(argPos++, newParam);
            }
            assert(buf.LastIndexOf(callCombiner) == exprPos + 1 + newParams.Length);
            if (!dropResult)
            {
                buf.ReplaceParameterByCopy(pos, exprPos + 1 + newParams.Length);
            }

            return(buf.EndEdit());
        }
        internal virtual LambdaForm CollectArgumentArrayForm(int pos, MethodHandle arrayCollector)
        {
            MethodType collectorType  = arrayCollector.Type();
            int        collectorArity = collectorType.ParameterCount();

            assert(arrayCollector.IntrinsicName() == Intrinsic.NEW_ARRAY);
            Class     arrayType   = collectorType.ReturnType();
            Class     elementType = arrayType.ComponentType;
            BasicType argType     = basicType(elementType);
            int       argTypeKey  = argType.ordinal();

            if (argType.basicTypeClass() != elementType)
            {
                // return null if it requires more metadata (like String[].class)
                if (!elementType.Primitive)
                {
                    return(null);
                }
                argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
            }
            assert(collectorType.ParameterList().Equals(Collections.NCopies(collectorArity, elementType)));
            Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY;
            Transform      key  = Transform.Of(kind, pos, collectorArity, argTypeKey);
            LambdaForm     form = GetInCache(key);

            if (form != null)
            {
                assert(form.Arity_Renamed == LambdaForm.Arity_Renamed - 1 + collectorArity);
                return(form);
            }
            LambdaFormBuffer buf = Buffer();

            buf.StartEdit();

            assert(pos + 1 <= LambdaForm.Arity_Renamed);
            assert(pos > 0);             // cannot filter the MH arg itself

            Name[] newParams = new Name[collectorArity];
            for (int i = 0; i < collectorArity; i++)
            {
                newParams[i] = new Name(pos + i, argType);
            }
            Name callCombiner = new Name(arrayCollector, (Object[])newParams);              //...

            // insert the new expression
            int exprPos = LambdaForm.Arity();

            buf.InsertExpression(exprPos, callCombiner);

            // insert new arguments
            int argPos = pos + 1;             // skip result parameter

            foreach (Name newParam in newParams)
            {
                buf.InsertParameter(argPos++, newParam);
            }
            assert(buf.LastIndexOf(callCombiner) == exprPos + newParams.Length);
            buf.ReplaceParameterByCopy(pos, exprPos + newParams.Length);

            form = buf.EndEdit();
            return(PutInCache(key, form));
        }
        /// <summary>
        /// Check the meta-factory arguments for errors </summary>
        /// <exception cref="LambdaConversionException"> if there are improper conversions </exception>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: void validateMetafactoryArgs() throws LambdaConversionException
        internal virtual void ValidateMetafactoryArgs()
        {
            switch (ImplKind)
            {
            case MethodHandleInfo.REF_invokeInterface:
            case MethodHandleInfo.REF_invokeVirtual:
            case MethodHandleInfo.REF_invokeStatic:
            case MethodHandleInfo.REF_newInvokeSpecial:
            case MethodHandleInfo.REF_invokeSpecial:
                break;

            default:
                throw new LambdaConversionException(string.Format("Unsupported MethodHandle kind: {0}", ImplInfo));
            }

            // Check arity: optional-receiver + captured + SAM == impl
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int implArity = implMethodType.parameterCount();
            int implArity = ImplMethodType.ParameterCount();
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int receiverArity = implIsInstanceMethod ? 1 : 0;
            int receiverArity = ImplIsInstanceMethod ? 1 : 0;
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int capturedArity = invokedType.parameterCount();
            int capturedArity = InvokedType.ParameterCount();
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int samArity = samMethodType.parameterCount();
            int samArity = SamMethodType.ParameterCount();
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int instantiatedArity = instantiatedMethodType.parameterCount();
            int instantiatedArity = InstantiatedMethodType.ParameterCount();

            if (implArity + receiverArity != capturedArity + samArity)
            {
                throw new LambdaConversionException(string.Format("Incorrect number of parameters for {0} method {1}; {2:D} captured parameters, {3:D} functional interface method parameters, {4:D} implementation parameters", ImplIsInstanceMethod ? "instance" : "static", ImplInfo, capturedArity, samArity, implArity));
            }
            if (instantiatedArity != samArity)
            {
                throw new LambdaConversionException(string.Format("Incorrect number of parameters for {0} method {1}; {2:D} instantiated parameters, {3:D} functional interface method parameters", ImplIsInstanceMethod ? "instance" : "static", ImplInfo, instantiatedArity, samArity));
            }
            foreach (MethodType bridgeMT in AdditionalBridges)
            {
                if (bridgeMT.ParameterCount() != samArity)
                {
                    throw new LambdaConversionException(string.Format("Incorrect number of parameters for bridge signature {0}; incompatible with {1}", bridgeMT, SamMethodType));
                }
            }

            // If instance: first captured arg (receiver) must be subtype of class where impl method is defined
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int capturedStart;
            int capturedStart;
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int samStart;
            int samStart;

            if (ImplIsInstanceMethod)
            {
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final Class receiverClass;
                Class receiverClass;

                // implementation is an instance method, adjust for receiver in captured variables / SAM arguments
                if (capturedArity == 0)
                {
                    // receiver is function parameter
                    capturedStart = 0;
                    samStart      = 1;
                    receiverClass = InstantiatedMethodType.ParameterType(0);
                }
                else
                {
                    // receiver is a captured variable
                    capturedStart = 1;
                    samStart      = 0;
                    receiverClass = InvokedType.ParameterType(0);
                }

                // check receiver type
                if (!receiverClass.IsSubclassOf(ImplDefiningClass))
                {
                    throw new LambdaConversionException(string.Format("Invalid receiver type {0}; not a subtype of implementation type {1}", receiverClass, ImplDefiningClass));
                }

                Class implReceiverClass = ImplMethod.Type().ParameterType(0);
                if (implReceiverClass != ImplDefiningClass && !receiverClass.IsSubclassOf(implReceiverClass))
                {
                    throw new LambdaConversionException(string.Format("Invalid receiver type {0}; not a subtype of implementation receiver type {1}", receiverClass, implReceiverClass));
                }
            }
            else
            {
                // no receiver
                capturedStart = 0;
                samStart      = 0;
            }

            // Check for exact match on non-receiver captured arguments
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int implFromCaptured = capturedArity - capturedStart;
            int implFromCaptured = capturedArity - capturedStart;

            for (int i = 0; i < implFromCaptured; i++)
            {
                Class implParamType     = ImplMethodType.ParameterType(i);
                Class capturedParamType = InvokedType.ParameterType(i + capturedStart);
                if (!capturedParamType.Equals(implParamType))
                {
                    throw new LambdaConversionException(string.Format("Type mismatch in captured lambda parameter {0:D}: expecting {1}, found {2}", i, capturedParamType, implParamType));
                }
            }
            // Check for adaptation match on SAM arguments
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int samOffset = samStart - implFromCaptured;
            int samOffset = samStart - implFromCaptured;

            for (int i = implFromCaptured; i < implArity; i++)
            {
                Class implParamType         = ImplMethodType.ParameterType(i);
                Class instantiatedParamType = InstantiatedMethodType.ParameterType(i + samOffset);
                if (!IsAdaptableTo(instantiatedParamType, implParamType, true))
                {
                    throw new LambdaConversionException(string.Format("Type mismatch for lambda argument {0:D}: {1} is not convertible to {2}", i, instantiatedParamType, implParamType));
                }
            }

            // Adaptation match: return type
            Class expectedType     = InstantiatedMethodType.ReturnType();
            Class actualReturnType = (ImplKind == MethodHandleInfo.REF_newInvokeSpecial) ? ImplDefiningClass : ImplMethodType.ReturnType();
            Class samReturnType    = SamMethodType.ReturnType();

            if (!IsAdaptableToAsReturn(actualReturnType, expectedType))
            {
                throw new LambdaConversionException(string.Format("Type mismatch for lambda return: {0} is not convertible to {1}", actualReturnType, expectedType));
            }
            if (!IsAdaptableToAsReturnStrict(expectedType, samReturnType))
            {
                throw new LambdaConversionException(string.Format("Type mismatch for lambda expected return: {0} is not convertible to {1}", expectedType, samReturnType));
            }
            foreach (MethodType bridgeMT in AdditionalBridges)
            {
                if (!IsAdaptableToAsReturnStrict(expectedType, bridgeMT.ReturnType()))
                {
                    throw new LambdaConversionException(string.Format("Type mismatch for lambda expected return: {0} is not convertible to {1}", expectedType, bridgeMT.ReturnType()));
                }
            }
        }