private void SetupAroundInfrastructure()
        {
            TopWrapper   = WrapOriginalMethod();
            _lastWrapper = CreateUnwrapMethod(TopWrapper);

            var topWrapperCut = PointCut.FromEmptyBody(TopWrapper.Body, OpCodes.Ret);

            //var args = new object[]{ arg1, agr2 }

            var argsvar = topWrapperCut.CreateVariable(TypeSystem.ObjectArray, null, c => c.LoadCallArgument(TopWrapper.Parameters.ToArray(), TypeSystem.ObjectArray));

            // ExecExternalWrapper
            var resultVar = topWrapperCut.CreateVariable(TypeSystem.Object,
                                                         null,
                                                         c =>
            {
                c.LoadSelfOntoStack();
                _topWrapperCallSite = c.InjectMethodCall(_lastWrapper, new object[] { argsvar });
            });

            //fill ref and out
            foreach (var param in TopWrapper.Parameters)
            {
                var index = TopWrapper.Parameters.IndexOf(param);

                // todo:: optimize and move to some syntax helper
                if (param.ParameterType.IsByReference)
                {
                    topWrapperCut.LoadParameterOntoStack(param, param.ParameterType);

                    topWrapperCut.LoadVariable(argsvar);
                    topWrapperCut.InsertBefore(topWrapperCut.CreateInstruction(OpCodes.Ldc_I4, index));
                    topWrapperCut.InsertBefore(topWrapperCut.CreateInstruction(OpCodes.Ldelem_Ref));

                    var elementType = ((ByReferenceType)param.ParameterType).ElementType;
                    topWrapperCut.BoxUnboxTryCastIfNeeded(TypeSystem.Object, elementType);
                    topWrapperCut.SaveIndirect(elementType);
                }
            }

            if (TopWrapper.ReturnType != TypeSystem.Void)
            {
                topWrapperCut.LoadVariable(resultVar);
                topWrapperCut.BoxUnboxTryCastIfNeeded(TypeSystem.Object, TargetMethod.ReturnType);
            }
        }
        public PointCut CreateNewAroundPoint()
        {
            if (_topWrapperCallSite == null)
            {
                SetupAroundInfrastructure();
            }

            var newWrapper = CopyMethodDefinition(AroundWrappedMethodPrefix + _wrapperNo + "_" + TopWrapper.Name,
                                                  TargetMethod.Attributes,
                                                  TypeSystem.Object);

            newWrapper.NoInlining = false;

            MarkDebuggerHidden(newWrapper);
            MarkCompilerGenerated(newWrapper);

            TargetMethod.DeclaringType.Methods.Add(newWrapper);

            var argsParam = new ParameterDefinition(TypeSystem.ObjectArray);

            newWrapper.Parameters.Add(argsParam);

            var tempPc = PointCut.FromEmptyBody(newWrapper.Body, OpCodes.Ret);

            //if (_topWrapper.ReturnType == TypeSystem.Void)
            //    tempPc.CreateVariableFromStack(TypeSystem.Object);
            //else if (_topWrapper.ReturnType.IsValueType)
            //    tempPc.InsertBefore(tempPc.CreateInstruction(OpCodes.Box, TargetMethod.Module.Import(_topWrapper.ReturnType)));

            var newWapperPoint = new WrapperPointCut(argsParam, _lastWrapper, ILProcessorFactory.GetOrCreateProcessor(newWrapper.Body), newWrapper.Body.Instructions.First());

            _lastWrapper = newWrapper;
            _wrapperNo++;

            //substiture top wrapper's call
            _topWrapperCallSite.InjectionPoint.Operand = newWrapper;

            return(newWapperPoint);
        }
        private MethodDefinition CreateUnwrapMethod(MethodDefinition originalMethod)
        {
            var unwrapMethod = CopyMethodDefinition(AroundUnwrappedMethodPrefix + originalMethod.Name,
                                                    originalMethod.Attributes,
                                                    TypeSystem.Object);

            unwrapMethod.NoInlining = false;

            MarkDebuggerHidden(unwrapMethod);
            MarkCompilerGenerated(unwrapMethod);

            originalMethod.DeclaringType.Methods.Add(unwrapMethod);

            var argsParam = new ParameterDefinition(TypeSystem.MakeArrayType(TypeSystem.Object));

            unwrapMethod.Parameters.Add(argsParam);

            var unwrapPoint = PointCut.FromEmptyBody(unwrapMethod.Body, OpCodes.Ret);

            unwrapPoint.LoadSelfOntoStack();

            var vars = new List <VariableDefinition>();

            foreach (var parameter in originalMethod.Parameters)
            {
                var varType = parameter.ParameterType;

                if (varType.IsByReference)
                {
                    varType = ((ByReferenceType)varType).ElementType;
                }

                vars.Add(
                    unwrapPoint.CreateVariable(varType,
                                               null,
                                               c =>
                {
                    c.LoadParameterOntoStack(argsParam);
                    c.InsertBefore(c.CreateInstruction(OpCodes.Ldc_I4, originalMethod.Parameters.IndexOf(parameter)));
                    c.InsertBefore(c.CreateInstruction(OpCodes.Ldelem_Ref));
                    c.BoxUnboxTryCastIfNeeded(TypeSystem.Object, varType);
                }));
            }

            foreach (var parameter in originalMethod.Parameters)
            {
                unwrapPoint.LoadVariable(vars[originalMethod.Parameters.IndexOf(parameter)], parameter.ParameterType);
            }

            unwrapPoint.InsertBefore(unwrapPoint.CreateInstruction(OpCodes.Call, TargetMethod.Module.Import(TargetMethod)));

            if (originalMethod.ReturnType == TypeSystem.Void)
            {
                unwrapPoint.LoadValueOntoStack <object>(null);
            }
            else if (originalMethod.ReturnType.IsValueType)
            {
                unwrapPoint.InsertBefore(unwrapPoint.CreateInstruction(OpCodes.Box, TargetMethod.Module.Import(originalMethod.ReturnType)));
            }

            foreach (var parameter in originalMethod.Parameters)
            {
                var index = originalMethod.Parameters.IndexOf(parameter);

                unwrapPoint.LoadParameterOntoStack(argsParam);
                unwrapPoint.InsertBefore(unwrapPoint.CreateInstruction(OpCodes.Ldc_I4, index));
                unwrapPoint.LoadVariable(vars[index], TypeSystem.Object);
                unwrapPoint.InsertBefore(unwrapPoint.CreateInstruction(OpCodes.Stelem_Ref));
            }

            return(unwrapMethod);
        }