#region License

/*
 * Copyright © 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

#region Imports

using System;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;

using Spring.Proxy;
using Spring.Util;

#endregion

namespace Spring.Aop.Framework.DynamicProxy
{
    /// <summary>
    /// Base class for AOP method builders that contains common functionalities.
    /// </summary>
    /// <author>Aleksandar Seovic</author>
    /// <author>Bruno Baia</author>
    public abstract class AbstractAopProxyMethodBuilder : AbstractProxyMethodBuilder
    {
        #region Fields

        /// <summary>
        /// The <see cref="IAopProxyTypeGenerator"/> implementation to use.
        /// </summary>
        protected IAopProxyTypeGenerator aopProxyGenerator;

        /// <summary>
        /// The dictionary to cache the list of target  
        /// <see cref="System.Reflection.MethodInfo"/>s.
        /// </summary>
        protected IDictionary targetMethods;

        /// <summary>
        /// The dictionary to cache the list of target  
        /// <see cref="System.Reflection.MethodInfo"/>s defined on the proxy.
        /// </summary>
        protected IDictionary onProxyTargetMethods;

        // variables

        /// <summary>
        /// The local variable to store the list of method interceptors.
        /// </summary>
        protected LocalBuilder interceptors;

        /// <summary>
        /// The local variable to store the target type being proxied.
        /// </summary>
        protected LocalBuilder targetType;

        /// <summary>
        /// The local variable to store method arguments.
        /// </summary>
        protected LocalBuilder arguments;

        /// <summary>
        /// The local variable to store the return value.
        /// </summary>
        protected LocalBuilder returnValue;

#if NET_2_0
        /// <summary>
        /// The local variable to store the closed generic method 
        /// when the target method is generic.
        /// </summary>
        protected LocalBuilder genericTargetMethod;

        /// <summary>
        /// The local variable to store the closed generic method 
        /// when the target method defined on the proxy is generic.
        /// </summary>
        protected LocalBuilder genericOnProxyTargetMethod;
#endif
        /// <summary>
        /// The field to cache the target <see cref="System.Reflection.MethodInfo"/>.
        /// </summary>
        protected FieldBuilder targetMethodCacheField;

        /// <summary>
        /// The field to cache the target <see cref="System.Reflection.MethodInfo"/> 
        /// defined on the proxy.
        /// </summary>
        protected FieldBuilder onProxyTargetMethodCacheField;
       
        // convinience fields

        /// <summary>
        /// Indicates if the method returns a value.
        /// </summary>
        protected bool methodReturnsValue;

        // private fields

        private static IDictionary ldindOpCodes;

        #endregion

        #region Constructor(s) / Destructor

        static AbstractAopProxyMethodBuilder()
        {
			ldindOpCodes = new Hashtable();
            ldindOpCodes[typeof(sbyte)] = OpCodes.Ldind_I1;
            ldindOpCodes[typeof(short)] = OpCodes.Ldind_I2;
            ldindOpCodes[typeof(int)] = OpCodes.Ldind_I4;
            ldindOpCodes[typeof(long)] = OpCodes.Ldind_I8;
            ldindOpCodes[typeof(byte)] = OpCodes.Ldind_U1;
            ldindOpCodes[typeof(ushort)] = OpCodes.Ldind_U2;
            ldindOpCodes[typeof(uint)] = OpCodes.Ldind_U4;
            ldindOpCodes[typeof(ulong)] = OpCodes.Ldind_I8;
            ldindOpCodes[typeof(float)] = OpCodes.Ldind_R4;
            ldindOpCodes[typeof(double)] = OpCodes.Ldind_R8;
            ldindOpCodes[typeof(char)] = OpCodes.Ldind_U2;
            ldindOpCodes[typeof(bool)] = OpCodes.Ldind_I1;
        }

        /// <summary>
        /// Creates a new instance of the method builder.
        /// </summary>
        /// <param name="typeBuilder">The type builder to use.</param>
        /// <param name="aopProxyGenerator">
        /// The <see cref="IAopProxyTypeGenerator"/> implementation to use.
        /// </param>
        /// <param name="explicitImplementation">
        /// <see langword="true"/> if the interface is to be
        /// implemented explicitly; otherwise <see langword="false"/>.
        /// </param>
        /// <param name="targetMethods">
        /// The dictionary to cache the list of target 
        /// <see cref="System.Reflection.MethodInfo"/>s.
        /// </param>
        protected AbstractAopProxyMethodBuilder(
            TypeBuilder typeBuilder, IAopProxyTypeGenerator aopProxyGenerator,
            bool explicitImplementation, IDictionary targetMethods)
            : this(typeBuilder, aopProxyGenerator, explicitImplementation, targetMethods, new Hashtable())
        {
        }

        /// <summary>
        /// Creates a new instance of the method builder.
        /// </summary>
        /// <param name="typeBuilder">The type builder to use.</param>
        /// <param name="aopProxyGenerator">
        /// The <see cref="IAopProxyTypeGenerator"/> implementation to use.
        /// </param>
        /// <param name="explicitImplementation">
        /// <see langword="true"/> if the interface is to be
        /// implemented explicitly; otherwise <see langword="false"/>.
        /// </param>
        /// <param name="targetMethods">
        /// The dictionary to cache the list of target 
        /// <see cref="System.Reflection.MethodInfo"/>s.
        /// </param>
        /// <param name="onProxyTargetMethods">
        /// The dictionary to cache the list of target  
        /// <see cref="System.Reflection.MethodInfo"/>s defined on the proxy.
        /// </param>
        protected AbstractAopProxyMethodBuilder(
            TypeBuilder typeBuilder, IAopProxyTypeGenerator aopProxyGenerator,
            bool explicitImplementation, IDictionary targetMethods, IDictionary onProxyTargetMethods)
            : base(typeBuilder, aopProxyGenerator, explicitImplementation)
        {
            this.aopProxyGenerator = aopProxyGenerator;
            this.targetMethods = targetMethods;
            this.onProxyTargetMethods = onProxyTargetMethods;
        }

        #endregion

        #region Protected Members

        /// <summary>
        /// Generates the proxy method.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        /// <param name="interfaceMethod">
        /// The interface definition of the method, if applicable.
        /// </param>
        protected override void GenerateMethod(
            ILGenerator il, MethodInfo method, MethodInfo interfaceMethod)
        {
            methodReturnsValue = (method.ReturnType != typeof(void));

            DeclareLocals(il, method);

            GenerateTargetMethodCacheField(il, method);
            GenerateOnProxyTargetMethodCacheField(il, method);

            BeginMethod(il, method);
            GenerateMethodLogic(il, method, interfaceMethod);
            EndMethod(il, method);
        }

        /// <summary>
        /// Generates unique method id for the cache field.
        /// </summary>
        /// <param name="method">The target method.</param>
        /// <returns>An unique method name.</returns>
        protected virtual string GenerateMethodCacheFieldId(MethodInfo method)
        {
            return "_m" + Guid.NewGuid().ToString("N");
        }

        /// <summary>
        /// Create static field that will cache target method.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The target method.</param>
        protected virtual void GenerateTargetMethodCacheField(
            ILGenerator il, MethodInfo method)
        {
            string methodId = GenerateMethodCacheFieldId(method);
            targetMethods.Add(methodId, method);

            targetMethodCacheField = typeBuilder.DefineField(methodId, typeof(MethodInfo), 
                FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly);

#if NET_2_0
            MakeGenericMethod(il, method, targetMethodCacheField, genericTargetMethod);
#endif
        }

        /// <summary>
        /// Create static field that will cache target method when defined on the proxy.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The target method.</param>
        protected virtual void GenerateOnProxyTargetMethodCacheField(
            ILGenerator il, MethodInfo method)
        {
        }

#if NET_2_0
        /// <summary>
        /// Create a closed generic method for the current call 
        /// if target method is a generic definition.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The target method.</param>
        /// <param name="methodCacheField">
        /// The field that contains the method generic definition
        /// </param>
        /// <param name="localMethod">
        /// The local variable to store the closed generic method.
        /// </param>
        protected void MakeGenericMethod(ILGenerator il, MethodInfo method,
            FieldBuilder methodCacheField, LocalBuilder localMethod)
        {
            // if target method is a generic definition,
            // create a closed generic method for the current call.
            if (method.IsGenericMethodDefinition)
            {
                Type[] genericArgs = method.GetGenericArguments();

                LocalBuilder typeArgs = il.DeclareLocal(typeof(Type[]));

                il.Emit(OpCodes.Ldsfld, methodCacheField);

                // specify array size and create an array
                il.Emit(OpCodes.Ldc_I4, genericArgs.Length);
                il.Emit(OpCodes.Newarr, typeof(Type));
                il.Emit(OpCodes.Stloc, typeArgs);

                // populate array with type arguments
                for (int i = 0; i < genericArgs.Length; i++)
                {
                    il.Emit(OpCodes.Ldloc, typeArgs);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Ldtoken, genericArgs[i]);
                    il.EmitCall(OpCodes.Call, References.GetTypeFromHandle, null);
                    il.Emit(OpCodes.Stelem_Ref);
                }

                il.Emit(OpCodes.Ldloc, typeArgs);
                il.Emit(OpCodes.Callvirt, References.MakeGenericMethod);
                il.Emit(OpCodes.Stloc, localMethod);
            }
        }
#endif

        /// <summary>
        /// Generates the IL instructions that pushes 
        /// the target type on stack.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        protected virtual void PushTargetType(ILGenerator il)
        {
            aopProxyGenerator.PushAdvisedProxy(il);
            il.Emit(OpCodes.Ldfld, References.TargetTypeField);
        }

        /// <summary>
        /// Generates the IL instructions that pushes  
        /// the current <see cref="Spring.Aop.Framework.DynamicProxy.AdvisedProxy"/> 
        /// instance on stack.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        protected virtual void PushAdvisedProxy(ILGenerator il)
        {
            aopProxyGenerator.PushAdvisedProxy(il);
        }

        /// <summary>
        /// Pushes the target <see cref="System.Reflection.MethodInfo"/> to stack.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        protected virtual void PushTargetMethodInfo(ILGenerator il, MethodInfo method)
        {
#if NET_2_0
            if (method.IsGenericMethodDefinition)
            {
                il.Emit(OpCodes.Ldloc, genericTargetMethod);
                return;
            }
#endif
            il.Emit(OpCodes.Ldsfld, targetMethodCacheField);
        }

        /// <summary>
        /// Pushes the target <see cref="System.Reflection.MethodInfo"/> defined on the proxy to stack.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        protected virtual void PushOnProxyTargetMethodInfo(ILGenerator il, MethodInfo method)
        {
            if (onProxyTargetMethodCacheField != null)
            {
#if NET_2_0
                if (method.IsGenericMethodDefinition)
                {
                    il.Emit(OpCodes.Ldloc, genericOnProxyTargetMethod);
                    return;
                }
#endif
                il.Emit(OpCodes.Ldsfld, onProxyTargetMethodCacheField);
            }
            else
            {
                il.Emit(OpCodes.Ldnull);
            }
        }

        /// <summary>
        /// Creates local variable declarations.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        protected virtual void DeclareLocals(ILGenerator il, MethodInfo method)
        {
            interceptors = il.DeclareLocal(typeof(IList));
            targetType = il.DeclareLocal(typeof(Type));
            arguments = il.DeclareLocal(typeof(Object[]));
            
#if NET_2_0
            if (method.IsGenericMethodDefinition)
            {
                genericTargetMethod = il.DeclareLocal(typeof(MethodInfo));
                genericOnProxyTargetMethod = il.DeclareLocal(typeof(MethodInfo));
            }
#endif
            if (methodReturnsValue)
            {
                returnValue = il.DeclareLocal(method.ReturnType);
            }

#if DEBUG
            interceptors.SetLocalSymInfo("interceptors");
            targetType.SetLocalSymInfo("targetType");
            arguments.SetLocalSymInfo("arguments");
#if NET_2_0
            if (method.IsGenericMethodDefinition)
            {
                genericTargetMethod.SetLocalSymInfo("genericTargetMethod");
                genericOnProxyTargetMethod.SetLocalSymInfo("genericOnProxyTargetMethod");
            }
#endif
            if (methodReturnsValue)
            {
                returnValue.SetLocalSymInfo("returnValue");
            }
#endif
        }

        /// <summary>
        /// Initializes local variables
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        protected virtual void BeginMethod(ILGenerator il, MethodInfo method)
        {
            Label jmpProxyNotExposed = il.DefineLabel();

            // set current proxy to this object
            PushAdvisedProxy(il);
            il.Emit(OpCodes.Ldfld, References.AdvisedField);
            il.EmitCall(OpCodes.Callvirt, References.ExposeProxyProperty, null);
            il.Emit(OpCodes.Brfalse_S, jmpProxyNotExposed);
            il.Emit(OpCodes.Ldarg_0);
            il.EmitCall(OpCodes.Call, References.PushProxyMethod, null);

            il.MarkLabel(jmpProxyNotExposed);

            // initialize targetType
            PushTargetType(il);
            il.Emit(OpCodes.Stloc, targetType);

            // initialize interceptors
            PushAdvisedProxy(il);
            il.Emit(OpCodes.Ldloc, targetType);
            PushTargetMethodInfo(il, method);
            il.EmitCall(OpCodes.Call, References.GetInterceptorsMethod, null);
            il.Emit(OpCodes.Stloc, interceptors);
        }

        /// <summary>
        /// Generates method logic.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        /// <param name="interfaceMethod">
        /// The interface definition of the method, if applicable.
        /// </param>
        protected virtual void GenerateMethodLogic(
            ILGenerator il, MethodInfo method, MethodInfo interfaceMethod)
        {
            Label jmpDirectCall = il.DefineLabel();
            Label jmpEndIf = il.DefineLabel();

            // check if there are any interceptors
            il.Emit(OpCodes.Ldloc, interceptors);
            il.EmitCall(OpCodes.Callvirt, References.CountProperty, null);
            il.Emit(OpCodes.Ldc_I4_0);

            // if not jump to direct call
            il.Emit(OpCodes.Ble, jmpDirectCall);

            // otherwise call Invoke and jump to method end
            CallInvoke(il, method);
            il.Emit(OpCodes.Br, jmpEndIf);

            // call method directly
            il.MarkLabel(jmpDirectCall);
            CallDirectProxiedMethod(il, method, interfaceMethod);
            if (methodReturnsValue)
            {
                // store return value, unboxing is not necessary because we called method directly
                il.Emit(OpCodes.Stloc, returnValue);
            }

            il.MarkLabel(jmpEndIf);

            if (methodReturnsValue)
            {
                if (!method.ReturnType.IsValueType)
                {
                    ProcessReturnValue(il, returnValue);
                }
            }
        }

        /// <summary>
        /// Calls method using Invoke
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        protected virtual void CallInvoke(ILGenerator il, MethodInfo method)
        {
            ParameterInfo[] parameters = method.GetParameters();

            SetupMethodArguments(il, method, parameters);

            PushAdvisedProxy(il);

            // setup parameters for call
            il.Emit(OpCodes.Ldarg_0);                       // proxy
            PushTarget(il);                                 // target
            il.Emit(OpCodes.Ldloc, targetType);             // target type
            PushTargetMethodInfo(il, method);               // method
            PushOnProxyTargetMethodInfo(il, method);        // method defined on proxy
            il.Emit(OpCodes.Ldloc, arguments);              // args
            il.Emit(OpCodes.Ldloc, interceptors);           // interceptors

            // call Invoke
            il.EmitCall(OpCodes.Call, References.InvokeMethod, null);

            // process return value
            if (methodReturnsValue)
            {
                EmitUnboxIfNeeded(il, method.ReturnType);
                il.Emit(OpCodes.Stloc, returnValue);
            }
            else
            {
                il.Emit(OpCodes.Pop);
            }

            // process byRef arguments
            for (int i = 0; i < parameters.Length; i++)
            {
                if (parameters[i].ParameterType.IsByRef)
                {
                    il.Emit(OpCodes.Ldarg_S, i + 1);
                    il.Emit(OpCodes.Ldloc, arguments);
                    il.Emit(OpCodes.Ldc_I4_S, i);
                    il.Emit(OpCodes.Ldelem_Ref);
                    Type type = parameters[i].ParameterType.GetElementType();
                    EmitUnboxIfNeeded(il, type);
                    EmitStoreValueIndirect(il, type);
                }
            }
        }

        /// <summary>
        /// Setup proxied method arguments.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        /// <param name="parameters">The method's parameters.</param>
        protected void SetupMethodArguments(
            ILGenerator il, MethodInfo method, ParameterInfo[] parameters)
        {
            if (parameters.Length > 0)
            {
                // specify array size and create an array
                il.Emit(OpCodes.Ldc_I4, parameters.Length);
                il.Emit(OpCodes.Newarr, typeof(Object));
                il.Emit(OpCodes.Stloc, arguments);

                // populate array with params
                for (int i = 0; i < parameters.Length; i++)
                {
                    Type type = parameters[i].ParameterType;

                    il.Emit(OpCodes.Ldloc, arguments);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Ldarg_S, i + 1);

                    // setup byRef arguments
                    if (type.IsByRef)
                    {
                        type = type.GetElementType();
                        EmitLoadValueIndirect(il, type);
                    }

#if NET_2_0
                    if (type.IsValueType || type.IsGenericParameter)
#else
                    if (type.IsValueType)
#endif
                    {
                        il.Emit(OpCodes.Box, type);
                    }

                    il.Emit(OpCodes.Stelem_Ref);
                }
            }
            else
            {
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Stloc, arguments);
            }
        }

        /// <summary>
        /// Calls proxied method directly.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        /// <param name="interfaceMethod">
        /// The interface definition of the method, if applicable.
        /// </param>
        protected abstract void CallDirectProxiedMethod(
            ILGenerator il, MethodInfo method, MethodInfo interfaceMethod);

        /// <summary>
        /// Ends method by returning return value if appropriate.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="method">The method to proxy.</param>
        protected virtual void EndMethod(ILGenerator il, MethodInfo method)
        {
            Label jmpProxyNotExposed = il.DefineLabel();

            // reset current proxy to old value
            PushAdvisedProxy(il);
            il.Emit(OpCodes.Ldfld, References.AdvisedField);
            il.EmitCall(OpCodes.Callvirt, References.ExposeProxyProperty, null);
            il.Emit(OpCodes.Brfalse_S, jmpProxyNotExposed);
            il.EmitCall(OpCodes.Call, References.PopProxyMethod, null);

            il.MarkLabel(jmpProxyNotExposed);

            if (methodReturnsValue)
            {
                il.Emit(OpCodes.Ldloc, returnValue);
            }
        }
        
        #endregion

        #region Reflection.Emit utility methods

        /// <summary>
        /// Emits MSIL instructions to load a value of the specified <paramref name="type"/> 
        /// onto the evaluation stack indirectly.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="type">The type of the value.</param>
        protected static void EmitLoadValueIndirect(ILGenerator il, Type type)
        {
            if (type.IsValueType)
            {
                if (type == typeof(int)) il.Emit(OpCodes.Ldind_I4);
                else if (type == typeof(uint)) il.Emit(OpCodes.Ldind_U4);
                else if (type == typeof(char)) il.Emit(OpCodes.Ldind_I2);
                else if (type == typeof(bool)) il.Emit(OpCodes.Ldind_I1);
                else if (type == typeof(float)) il.Emit(OpCodes.Ldind_R4);
                else if (type == typeof(double)) il.Emit(OpCodes.Ldind_R8);
                else if (type == typeof(short)) il.Emit(OpCodes.Ldind_I2);
                else if (type == typeof(ushort)) il.Emit(OpCodes.Ldind_U2);
                else if (type == typeof(long) || type == typeof(ulong)) il.Emit(OpCodes.Ldind_I8);
                else il.Emit(OpCodes.Ldobj, type);
            }
            else
            {
                il.Emit(OpCodes.Ldind_Ref);
            }
        }

        /// <summary>
        /// Emit MSIL instructions to store a value of the specified <paramref name="type"/> 
        /// at a supplied address.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="type">The type of the value.</param>
        protected static void EmitStoreValueIndirect(ILGenerator il, Type type)
        {
            if (type.IsValueType)
            {
                if (type.IsEnum) EmitStoreValueIndirect(il, Enum.GetUnderlyingType(type));
                else if (type == typeof(int)) il.Emit(OpCodes.Stind_I4);
                else if (type == typeof(short)) il.Emit(OpCodes.Stind_I2);
                else if (type == typeof(long) || type == typeof(ulong)) il.Emit(OpCodes.Stind_I8);
                else if (type == typeof(char)) il.Emit(OpCodes.Stind_I2);
                else if (type == typeof(bool)) il.Emit(OpCodes.Stind_I1);
                else if (type == typeof(float)) il.Emit(OpCodes.Stind_R4);
                else if (type == typeof(double)) il.Emit(OpCodes.Stind_R8);
                else il.Emit(OpCodes.Stobj, type);
            }
            else
            {
                il.Emit(OpCodes.Stind_Ref);
            }
        }

        /// <summary>
        /// Emits MSIL instructions to convert the boxed representation 
        /// of the supplied <paramref name="type"/> to its unboxed form.
        /// </summary>
        /// <param name="il">The IL generator to use.</param>
        /// <param name="type">The type specified in the instruction.</param>
        protected static void EmitUnboxIfNeeded(ILGenerator il, Type type)
        {
#if NET_2_0
            if (type.IsValueType || type.IsGenericParameter)
            {
                il.Emit(OpCodes.Unbox_Any, type);
            }
#else
            if (type.IsValueType)
            {
                il.Emit(OpCodes.Unbox, type);
                il.Emit(OpCodes.Ldobj, type);
            }
#endif
        }

        #endregion
    }

    #region References helper class definition

    internal struct References
    {
        // fields
        public static readonly FieldInfo AdvisedField =
            typeof(AdvisedProxy).GetField("m_advised", BindingFlags.Instance | BindingFlags.Public);

        public static readonly FieldInfo TargetTypeField =
            typeof(AdvisedProxy).GetField("m_targetType", BindingFlags.Instance | BindingFlags.Public);

        public static readonly FieldInfo IntroductionsField =
            typeof(AdvisedProxy).GetField("m_introductions", BindingFlags.Instance | BindingFlags.Public);

        public static readonly FieldInfo TargetSourceWrapperField =
            typeof(AdvisedProxy).GetField("m_targetSourceWrapper", BindingFlags.Instance | BindingFlags.Public);

        // constructors
        public static readonly ConstructorInfo BaseCompositionAopProxyConstructor =
            typeof(BaseCompositionAopProxy).GetConstructor(new Type[] { typeof(IAdvised) });

        public static readonly ConstructorInfo BaseCompositionAopProxySerializationConstructor =
            typeof(BaseCompositionAopProxy).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null,
                                                            new Type[] {typeof(SerializationInfo), typeof(StreamingContext)},
                                                            null);

        public static readonly ConstructorInfo AdvisedProxyConstructor =
            typeof(AdvisedProxy).GetConstructor(new Type[] { typeof(IAdvised), typeof(IAopProxy) });

        public static readonly ConstructorInfo AdvisedProxySerializationConstructor =
            typeof(AdvisedProxy).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null,
                                                 new Type[] {typeof (SerializationInfo), typeof (StreamingContext)},
                                                 null);
        public static readonly ConstructorInfo ObjectConstructor =
            typeof(Object).GetConstructor(Type.EmptyTypes);

        // methods
        public static readonly MethodInfo PushProxyMethod =
            typeof(AopContext).GetMethod("PushProxy", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(Object) }, null);

        public static readonly MethodInfo PopProxyMethod =
            typeof(AopContext).GetMethod("PopProxy", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null);

        public static readonly MethodInfo InvokeMethod =
            typeof(AdvisedProxy).GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(Object), typeof(Object), typeof(Type), typeof(MethodInfo), typeof(MethodInfo), typeof(Object[]), typeof(IList) }, null);

        public static readonly MethodInfo GetInterceptorsMethod =
            typeof(AdvisedProxy).GetMethod("GetInterceptors", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(Type), typeof(MethodInfo) }, null);

        public static readonly MethodInfo GetTargetMethod =
            typeof(ITargetSourceWrapper).GetMethod("GetTarget", Type.EmptyTypes);

        public static readonly MethodInfo GetTypeMethod =
            typeof(Object).GetMethod("GetType", Type.EmptyTypes);

        public static readonly MethodInfo GetTypeFromHandle = 
            typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) });

#if NET_2_0
        public static readonly MethodInfo MakeGenericMethod =
            typeof(MethodInfo).GetMethod("MakeGenericMethod", new Type[] { typeof(Type[]) });
#endif

        public static readonly MethodInfo DisposeMethod =
            typeof(IDisposable).GetMethod("Dispose", Type.EmptyTypes);

        public static readonly MethodInfo AddSerializationValue =
            typeof(SerializationInfo).GetMethod("AddValue", new Type[] { typeof(string), typeof(object) });

        public static readonly MethodInfo GetSerializationValue =
            typeof(SerializationInfo).GetMethod("GetValue", new Type[] { typeof(string), typeof(Type) });

        // properties
        public static readonly MethodInfo ExposeProxyProperty =
            typeof(IAdvised).GetProperty("ExposeProxy", typeof(Boolean)).GetGetMethod();

        public static readonly MethodInfo CountProperty =
            typeof(ICollection).GetProperty("Count", typeof(Int32)).GetGetMethod();
    }

    #endregion
}
