File: System\ServiceModel\Dispatcher\InvokerUtil.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.ServiceModel.Dispatcher
{
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Runtime;
    using System.Security;
    using System.Security.Permissions;
    using System.ServiceModel.Description;
 
    delegate object InvokeDelegate(object target, object[] inputs, object[] outputs);
    delegate IAsyncResult InvokeBeginDelegate(object target, object[] inputs, AsyncCallback asyncCallback, object state);
    delegate object InvokeEndDelegate(object target, object[] outputs, IAsyncResult result);
    delegate object CreateInstanceDelegate();
 
    sealed class InvokerUtil
    {
        [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that was produced within an assert.")]
        [SecurityCritical]
        CriticalHelper helper;
 
        [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
            Safe = "Doesn't leak anything.")]
        [SecuritySafeCritical]
        public InvokerUtil()
        {
            helper = new CriticalHelper();
        }
 
        [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.",
            Safe = "Resultant delegate is self-contained and safe for general consumption, nothing else leaks.")]
        [SecuritySafeCritical]
        internal CreateInstanceDelegate GenerateCreateInstanceDelegate(Type type, ConstructorInfo constructor)
        {
            return helper.GenerateCreateInstanceDelegate(type, constructor);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.",
            Safe = "Resultant delegate is self-contained and safe for general consumption, parameter counts are safe, nothing else leaks.")]
        [SecuritySafeCritical]
        internal InvokeDelegate GenerateInvokeDelegate(MethodInfo method, out int inputParameterCount, out int outputParameterCount)
        {
            return helper.GenerateInvokeDelegate(method, out inputParameterCount, out outputParameterCount);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.",
            Safe = "Resultant delegate is self-contained and safe for general consumption, parameter counts are safe, nothing else leaks.")]
        [SecuritySafeCritical]
        internal InvokeBeginDelegate GenerateInvokeBeginDelegate(MethodInfo method, out int inputParameterCount)
        {
            return helper.GenerateInvokeBeginDelegate(method, out inputParameterCount);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical helper class 'CriticalHelper'.",
            Safe = "Resultant delegate is self-contained and safe for general consumption, parameter counts are safe, nothing else leaks.")]
        [SecuritySafeCritical]
        internal InvokeEndDelegate GenerateInvokeEndDelegate(MethodInfo method, out int outputParameterCount)
        {
            return helper.GenerateInvokeEndDelegate(method, out outputParameterCount);
        }
        
        [Fx.Tag.SecurityNote(Critical = "Handles all aspects of IL generation including initializing the DynamicMethod, which requires an elevation."
            + "Since the ILGenerator is created under an elevation, we lock down access to every aspect of the generation.")]
#pragma warning disable 618 // have not moved to the v4 security model yet
        [SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
        class CriticalHelper
        {
            static Type TypeOfObject = typeof(object);
 
            CodeGenerator ilg;
 
            internal CreateInstanceDelegate GenerateCreateInstanceDelegate(Type type, ConstructorInfo constructor)
            {
                bool requiresMemberAccess = !IsTypeVisible(type) || ConstructorRequiresMemberAccess(constructor);
 
                this.ilg = new CodeGenerator();
                try
                {
                    ilg.BeginMethod("Create" + type.FullName, typeof(CreateInstanceDelegate), requiresMemberAccess);
                }
                catch (SecurityException securityException)
                {
                    if (requiresMemberAccess && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
                    {
                        DiagnosticUtility.TraceHandledException(securityException, TraceEventType.Warning);
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new SecurityException(SR.GetString(
                                    SR.PartialTrustServiceCtorNotVisible,
                                    type.FullName)));
                    }
                    else
                    {
                        throw;
                    }
                }
 
                if (type.IsValueType)
                {
                    LocalBuilder instanceLocal = ilg.DeclareLocal(type, type.Name + "Instance");
                    ilg.LoadZeroValueIntoLocal(type, instanceLocal);
                    ilg.Load(instanceLocal);
                }
                else
                {
                    ilg.New(constructor);
                }
                ilg.ConvertValue(type, ilg.CurrentMethod.ReturnType);
                return (CreateInstanceDelegate)ilg.EndMethod();
            }
 
            internal InvokeDelegate GenerateInvokeDelegate(MethodInfo method, out int inputParameterCount, out int outputParameterCount)
            {
                bool requiresMemberAccess = MethodRequiresMemberAccess(method);
 
                this.ilg = new CodeGenerator();
                try
                {
                    this.ilg.BeginMethod("SyncInvoke" + method.Name, typeof(InvokeDelegate), requiresMemberAccess);
                }
                catch (SecurityException securityException)
                {
                    if (requiresMemberAccess && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
                    {
                        DiagnosticUtility.TraceHandledException(securityException, TraceEventType.Warning);
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new SecurityException(SR.GetString(
                                    SR.PartialTrustServiceMethodNotVisible,
                                    method.DeclaringType.FullName,
                                    method.Name)));
                    }
                    else
                    {
                        throw;
                    }
                }
 
                ArgBuilder targetArg = ilg.GetArg(0);
                ArgBuilder inputParametersArg = ilg.GetArg(1);
                ArgBuilder outputParametersArg = ilg.GetArg(2);
 
                ParameterInfo[] parameters = method.GetParameters();
                LocalBuilder returnLocal = ilg.DeclareLocal(ilg.CurrentMethod.ReturnType, "returnParam");
                LocalBuilder[] parameterLocals = new LocalBuilder[parameters.Length];
                DeclareParameterLocals(parameters, parameterLocals);
 
                LoadInputParametersIntoLocals(parameters, parameterLocals, inputParametersArg, out inputParameterCount);
                LoadTarget(targetArg, method.ReflectedType);
                LoadParameters(parameters, parameterLocals);
                InvokeMethod(method, returnLocal);
                LoadOutputParametersIntoArray(parameters, parameterLocals, outputParametersArg, out outputParameterCount);
 
                ilg.Load(returnLocal);
                return (InvokeDelegate)this.ilg.EndMethod();
            }
 
            internal InvokeBeginDelegate GenerateInvokeBeginDelegate(MethodInfo method, out int inputParameterCount)
            {
                bool requiresMemberAccess = MethodRequiresMemberAccess(method);
 
                this.ilg = new CodeGenerator();
                try
                {
                    this.ilg.BeginMethod("AsyncInvokeBegin" + method.Name, typeof(InvokeBeginDelegate), requiresMemberAccess);
                }
                catch (SecurityException securityException)
                {
                    if (requiresMemberAccess && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
                    {
                        DiagnosticUtility.TraceHandledException(securityException, TraceEventType.Warning);
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new SecurityException(SR.GetString(
                                    SR.PartialTrustServiceMethodNotVisible,
                                    method.DeclaringType.FullName,
                                    method.Name)));
                    }
                    else
                    {
                        throw;
                    }
                }
 
                ArgBuilder targetArg = ilg.GetArg(0);
                ArgBuilder inputParametersArg = ilg.GetArg(1);
                ArgBuilder callbackArg = ilg.GetArg(2);
                ArgBuilder stateArg = ilg.GetArg(3);
 
                ParameterInfo[] parameters = method.GetParameters();
                LocalBuilder returnLocal = ilg.DeclareLocal(ilg.CurrentMethod.ReturnType, "returnParam");
                LocalBuilder[] parameterLocals = new LocalBuilder[parameters.Length - 2];
                DeclareParameterLocals(parameters, parameterLocals);
 
                LoadInputParametersIntoLocals(parameters, parameterLocals, inputParametersArg, out inputParameterCount);
                LoadTarget(targetArg, method.ReflectedType);
                LoadParameters(parameters, parameterLocals);
                ilg.Load(callbackArg);
                ilg.Load(stateArg);
                InvokeMethod(method, returnLocal);
 
                ilg.Load(returnLocal);
                return (InvokeBeginDelegate)this.ilg.EndMethod();
            }
 
            internal InvokeEndDelegate GenerateInvokeEndDelegate(MethodInfo method, out int outputParameterCount)
            {
                bool requiresMemberAccess = MethodRequiresMemberAccess(method);
 
                this.ilg = new CodeGenerator();
                try
                {
                    this.ilg.BeginMethod("AsyncInvokeEnd" + method.Name, typeof(InvokeEndDelegate), requiresMemberAccess);
                }
                catch (SecurityException securityException)
                {
                    if (requiresMemberAccess && securityException.PermissionType.Equals(typeof(ReflectionPermission)))
                    {
                        DiagnosticUtility.TraceHandledException(securityException, TraceEventType.Warning);
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                            new SecurityException(SR.GetString(
                                    SR.PartialTrustServiceMethodNotVisible,
                                    method.DeclaringType.FullName,
                                    method.Name)));
                    }
                    else
                    {
                        throw;
                    }
                }
 
                ArgBuilder targetArg = ilg.GetArg(0);
                ArgBuilder outputParametersArg = ilg.GetArg(1);
                ArgBuilder resultArg = ilg.GetArg(2);
 
                ParameterInfo[] parameters = method.GetParameters();
                LocalBuilder returnLocal = ilg.DeclareLocal(ilg.CurrentMethod.ReturnType, "returnParam");
                LocalBuilder[] parameterLocals = new LocalBuilder[parameters.Length - 1];
                DeclareParameterLocals(parameters, parameterLocals);
 
                LoadZeroValueInputParametersIntoLocals(parameters, parameterLocals);
                LoadTarget(targetArg, method.ReflectedType);
                LoadParameters(parameters, parameterLocals);
                ilg.Load(resultArg);
                InvokeMethod(method, returnLocal);
                LoadOutputParametersIntoArray(parameters, parameterLocals, outputParametersArg, out outputParameterCount);
 
                ilg.Load(returnLocal);
                return (InvokeEndDelegate)this.ilg.EndMethod();
            }
 
            void DeclareParameterLocals(ParameterInfo[] parameters, LocalBuilder[] parameterLocals)
            {
                for (int i = 0; i < parameterLocals.Length; i++)
                {
                    parameterLocals[i] = ilg.DeclareLocal(TypeLoader.GetParameterType(parameters[i]), "param" + i.ToString(CultureInfo.InvariantCulture));
                }
            }
 
            void LoadInputParametersIntoLocals(ParameterInfo[] parameters, LocalBuilder[] parameterLocals, ArgBuilder inputParametersArg, out int inputParameterCount)
            {
                inputParameterCount = 0;
                for (int i = 0; i < parameterLocals.Length; i++)
                {
                    if (ServiceReflector.FlowsIn(parameters[i]))
                    {
                        Type parameterType = parameterLocals[i].LocalType;
                        ilg.LoadArrayElement(inputParametersArg, inputParameterCount);
                        if (!parameterType.IsValueType)
                        {
                            ilg.ConvertValue(TypeOfObject, parameterType);
                            ilg.Store(parameterLocals[i]);
                        }
                        else
                        {
                            ilg.Dup();
                            ilg.If();
                            ilg.ConvertValue(TypeOfObject, parameterType);
                            ilg.Store(parameterLocals[i]);
                            ilg.Else();
                            ilg.Pop();
                            ilg.LoadZeroValueIntoLocal(parameterType, parameterLocals[i]);
                            ilg.EndIf();
                        }
                        inputParameterCount++;
                    }
                }
            }
 
            void LoadZeroValueInputParametersIntoLocals(ParameterInfo[] parameters, LocalBuilder[] parameterLocals)
            {
                for (int i = 0; i < parameterLocals.Length; i++)
                {
                    if (ServiceReflector.FlowsIn(parameters[i]))
                    {
                        ilg.LoadZeroValueIntoLocal(parameterLocals[i].LocalType, parameterLocals[i]);
                    }
                }
            }
 
            void LoadTarget(ArgBuilder targetArg, Type targetType)
            {
                ilg.Load(targetArg);
                ilg.ConvertValue(targetArg.ArgType, targetType);
                if (targetType.IsValueType)
                {
                    LocalBuilder targetLocal = ilg.DeclareLocal(targetType, "target");
                    ilg.Store(targetLocal);
                    ilg.LoadAddress(targetLocal);
                }
            }
 
            void LoadParameters(ParameterInfo[] parameters, LocalBuilder[] parameterLocals)
            {
                for (int i = 0; i < parameterLocals.Length; i++)
                {
                    if (parameters[i].ParameterType.IsByRef)
                        ilg.Ldloca(parameterLocals[i]);
                    else
                        ilg.Ldloc(parameterLocals[i]);
                }
            }
 
            void InvokeMethod(MethodInfo method, LocalBuilder returnLocal)
            {
                ilg.Call(method);
                if (method.ReturnType == typeof(void))
                    ilg.Load(null);
                else
                    ilg.ConvertValue(method.ReturnType, ilg.CurrentMethod.ReturnType);
                ilg.Store(returnLocal);
            }
 
            void LoadOutputParametersIntoArray(ParameterInfo[] parameters, LocalBuilder[] parameterLocals, ArgBuilder outputParametersArg, out int outputParameterCount)
            {
                outputParameterCount = 0;
                for (int i = 0; i < parameterLocals.Length; i++)
                {
                    if (ServiceReflector.FlowsOut(parameters[i]))
                    {
                        ilg.Load(outputParametersArg);
                        ilg.Load(outputParameterCount);
                        ilg.Load(parameterLocals[i]);
                        ilg.ConvertValue(parameterLocals[i].LocalType, TypeOfObject);
                        ilg.Stelem(TypeOfObject);
                        outputParameterCount++;
                    }
                }
            }
 
            static bool IsTypeVisible(Type t)
            {
                if (t.Module == typeof(InvokerUtil).Module)
                    return true;
 
                if (!t.IsVisible)
                    return false;
 
                foreach (Type genericType in t.GetGenericArguments())
                {
                    if (!genericType.IsGenericParameter && !IsTypeVisible(genericType))
                        return false;
                }
 
                return true;
            }
 
            static bool ConstructorRequiresMemberAccess(ConstructorInfo ctor)
            {
                return ctor != null && (!ctor.IsPublic || !IsTypeVisible(ctor.DeclaringType)) && ctor.Module != typeof(InvokerUtil).Module;
            }
 
            static bool MethodRequiresMemberAccess(MethodInfo method)
            {
                return method != null && (!method.IsPublic || !IsTypeVisible(method.DeclaringType)) && method.Module != typeof(InvokerUtil).Module;
            }
        }
    }
}