#region COPYRIGHT
//
//     THIS IS GENERATED BY TEMPLATE
//     
//     AUTHOR  :     ROYE
//     DATE       :     2010
//
//     COPYRIGHT (C) 2010, TIANXIAHOTEL TECHNOLOGIES CO., LTD. ALL RIGHTS RESERVED.
//
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Reflection.Emit
{
    internal class CtorInvocationEmitter : InvocationEmitter
    {
        public CtorInvocationEmitter(ConstructorInfo ctorInfo, Flags bindingFlags)
            : this(ctorInfo.DeclaringType, bindingFlags, ctorInfo.GetParameters().ToTypeArray(), ctorInfo)
        {
        }

        public CtorInvocationEmitter(Type targetType, Flags bindingFlags, Type[] paramTypes)
            : this(targetType, bindingFlags, paramTypes, null)
        {
        }

        private CtorInvocationEmitter(Type targetType, Flags flags, Type[] parameterTypes, ConstructorInfo ctorInfo)
            : base(new CallInfo(targetType, flags, MemberTypes.Constructor, targetType.Name, parameterTypes, ctorInfo))
        {
        }

        protected internal override DynamicMethod CreateDynamicMethod()
        {
            return CreateDynamicMethod("ctor", CallInfo.TargetType, Constants.ObjectType, new[] { Constants.ObjectType });
        }

        protected internal override Delegate CreateDelegate()
        {
            if (CallInfo.IsTargetTypeStruct && CallInfo.HasNoParam) // no-arg struct needs special initialization
            {
                Generator.DeclareLocal(CallInfo.TargetType);      // TargetType tmp
                Generator.ldloca_s(0)                                           // &tmp
                    .initobj(CallInfo.TargetType)                            // init_obj(&tmp)
                    .ldloc_0                                                            // load tmp
                    .end();
            }
            else if (CallInfo.TargetType.IsArray)
            {
                Generator.ldarg_0                                        // load args[] (method arguments)
                    .ldc_i4_0                                                  // load 0
                    .ldelem_ref                                               // load args[0] (length)
                    .unbox_any(typeof(int))                            // unbox stack
                    .newarr(CallInfo.TargetType.GetElementType());   // new T[args[0]]
            }
            else
            {
                ConstructorInfo ctorInfo = LookupUtils.GetConstructor(CallInfo);
                byte startUsableLocalIndex = 0;
                if (CallInfo.HasRefParam)
                {
                    startUsableLocalIndex = CreateLocalsForByRefParams(0, ctorInfo); // create by_ref_locals from argument array
                    Generator.DeclareLocal(CallInfo.TargetType);                     // TargetType tmp;
                }

                PushParamsOrLocalsToStack(0);               // push arguments and by_ref_locals
                Generator.newobj(ctorInfo);                 // ctor (<stack>)

                if (CallInfo.HasRefParam)
                {
                    Generator.stloc(startUsableLocalIndex); // tmp = <stack>;
                    AssignByRefParamsToArray(0);            // store by_ref_locals back to argument array
                    Generator.ldloc(startUsableLocalIndex); // tmp
                }
            }
            Generator.boxIfValueType(CallInfo.TargetType)
                .ret();                                // return (box)<stack>;

            return Method.CreateDelegate(typeof(ConstructorInvoker));
        }
    }
}
