Exemplo n.º 1
0
        /// <summary>
        /// Writes the pointcut body.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <param name="innerMethod">The inner method.</param>
        /// <param name="abstractedTarget">if set to <c>true</c> [abstracted target].</param>
        /// <exception cref="System.InvalidOperationException">
        /// </exception>
        private void WritePointcutBody(MethodDefinition method, MethodDefinition innerMethod, bool abstractedTarget)
        {
            var moduleDefinition = method.Module;

            // now empty the old one and make it call the inner method...
            if (method.Body == null)
            {
                method.Body = new MethodBody(method);
            }
            method.Body.InitLocals = true;
            method.Body.Instructions.Clear();
            method.Body.Variables.Clear();
            method.Body.ExceptionHandlers.Clear();
            var instructions = new Instructions(method.Body.Instructions, method.Module);

            var isStatic = method.Attributes.HasFlag(MethodAttributes.Static);

            // parameters
            VariableDefinition parametersVariable = null;

            if (method.Parameters.Count > 0)
            {
                parametersVariable = new VariableDefinition("parameters", moduleDefinition.SafeImport(typeof(object[])));
                method.Body.Variables.Add(parametersVariable);

                instructions.EmitLdc(method.Parameters.Count);
                instructions.Emit(OpCodes.Newarr, moduleDefinition.SafeImport(typeof(object)));
                instructions.EmitStloc(parametersVariable);
                // setups parameters array
                for (int parameterIndex = 0; parameterIndex < method.Parameters.Count; parameterIndex++)
                {
                    var parameter = method.Parameters[parameterIndex];
                    // we don't care about output parameters
                    if (!parameter.IsOut)
                    {
                        instructions.EmitLdloc(parametersVariable); // array
                        instructions.EmitLdc(parameterIndex);       // array index
                        instructions.EmitLdarg(parameter);          // loads given parameter...
                        var parameterType = parameter.ParameterType;
                        if (parameter.ParameterType.IsByReference)  // ...if ref, loads it as referenced value
                        {
                            parameterType = parameter.ParameterType.GetElementType();
                            instructions.EmitLdind(parameterType);
                        }
                        instructions.EmitBoxIfNecessary(parameterType); // ... and boxes it
                        instructions.Emit(OpCodes.Stelem_Ref);
                    }
                }
            }

            // if method has generic parameters, we also pass them to Proceed method
            VariableDefinition genericParametersVariable = null;
            // on static methods from genetic type, we also record the generic parameters type
            //var typeGenericParametersCount = isStatic ? method.DeclaringType.GenericParameters.Count : 0;
            var typeGenericParametersCount = method.DeclaringType.GenericParameters.Count;

            if (typeGenericParametersCount > 0 || method.HasGenericParameters)
            {
                //IL_0001: ldtoken !!T
                //IL_0006: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
                genericParametersVariable = new VariableDefinition("genericParameters", moduleDefinition.SafeImport(typeof(Type[])));
                method.Body.Variables.Add(genericParametersVariable);

                instructions.EmitLdc(typeGenericParametersCount + method.GenericParameters.Count);
                instructions.Emit(OpCodes.Newarr, moduleDefinition.SafeImport(typeof(Type)));
                instructions.EmitStloc(genericParametersVariable);

                var genericParameters = new List <GenericParameter>();
                for (int typeGenericParameterIndex = 0; typeGenericParameterIndex < typeGenericParametersCount; typeGenericParameterIndex++)
                {
                    genericParameters.Add(method.DeclaringType.GenericParameters[typeGenericParameterIndex]);
                }
                genericParameters.AddRange(method.GenericParameters);

                for (int genericParameterIndex = 0; genericParameterIndex < genericParameters.Count; genericParameterIndex++)
                {
                    instructions.EmitLdloc(genericParametersVariable); // array
                    instructions.EmitLdc(genericParameterIndex);       // array index
                    instructions.Emit(OpCodes.Ldtoken, genericParameters[genericParameterIndex]);
                    // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
                    instructions.Emit(OpCodes.Call, ReflectionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(new RuntimeTypeHandle())));
                    instructions.Emit(OpCodes.Stelem_Ref);
                }
            }

            // null or instance
            instructions.Emit(isStatic ? OpCodes.Ldnull : OpCodes.Ldarg_0);
            // to fix peverify 0x80131854
            if (!isStatic && method.IsConstructor)
            {
                instructions.Emit(OpCodes.Castclass, typeof(object));
            }

            // parameters
            if (parametersVariable != null)
            {
                instructions.EmitLdloc(parametersVariable);
            }
            else
            {
                instructions.Emit(OpCodes.Ldnull);
            }

            // methods...
            // ... target
            // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
            instructions.Emit(OpCodes.Call, ReflectionUtility.GetMethodInfo(() => MethodBase.GetCurrentMethod()));

            // ... inner... If provided
            if (innerMethod != null)
            {
                // if type is generic, this is a bit more complex, because we need to pass the type
                if (method.DeclaringType.HasGenericParameters)
                {
                    // we want to reuse the MethodBase.GetCurrentMethod() result
                    // so it is stored into a variable, whose property DeclaringType is invoked later
                    var currentMethodVariable = new VariableDefinition("currentMethod", moduleDefinition.SafeImport(typeof(MethodBase)));
                    method.Body.Variables.Add(currentMethodVariable);
                    instructions.EmitStloc(currentMethodVariable);
                    instructions.EmitLdloc(currentMethodVariable);

                    instructions.Emit(OpCodes.Ldtoken, innerMethod);
                    instructions.EmitLdloc(currentMethodVariable);
                    instructions.Emit(OpCodes.Callvirt, ReflectionUtility.GetMethodInfo((Type t) => t.DeclaringType));
                    instructions.Emit(OpCodes.Callvirt, ReflectionUtility.GetMethodInfo((Type t) => t.TypeHandle));
                    // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
                    instructions.Emit(OpCodes.Call, ReflectionUtility.GetMethodInfo(() => MethodBase.GetMethodFromHandle(new RuntimeMethodHandle(), new RuntimeTypeHandle())));
                }
                else
                {
                    instructions.Emit(OpCodes.Ldtoken, innerMethod);
                    // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
                    instructions.Emit(OpCodes.Call, ReflectionUtility.GetMethodInfo(() => MethodBase.GetMethodFromHandle(new RuntimeMethodHandle())));
                }
            }
            else
            {
                instructions.Emit(OpCodes.Ldnull);
            }

            // abstracted target
            instructions.Emit(abstractedTarget ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);

            if (genericParametersVariable != null)
            {
                instructions.EmitLdloc(genericParametersVariable);
            }
            else
            {
                instructions.Emit(OpCodes.Ldnull);
            }

            // invoke the method
            var invocationType = TypeResolver.Resolve(moduleDefinition, typeof(Invocation));

            if (invocationType == null)
            {
                throw new InvalidOperationException();
            }
            var proceedMethodReference = invocationType.GetMethods().SingleOrDefault(m => m.IsStatic && m.Name == nameof(Invocation.ProceedAdvice));

            if (proceedMethodReference == null)
            {
                throw new InvalidOperationException();
            }
            var proceedMethod = moduleDefinition.SafeImport(proceedMethodReference);

            instructions.Emit(OpCodes.Call, proceedMethod);

            // get return value
            if (!method.ReturnType.SafeEquivalent(moduleDefinition.SafeImport(typeof(void))))
            {
                instructions.EmitUnboxOrCastIfNecessary(method.ReturnType);
            }
            else
            {
                instructions.Emit(OpCodes.Pop); // if no return type, ignore Proceed() result
            }
            // loads back out/ref parameters
            for (int parameterIndex = 0; parameterIndex < method.Parameters.Count; parameterIndex++)
            {
                var parameter = method.Parameters[parameterIndex];
                if (parameter.ParameterType.IsByReference)
                {
                    instructions.EmitLdarg(parameter);             // loads given parameter (it is a ref)
                    instructions.EmitLdloc(parametersVariable);    // array
                    instructions.EmitLdc(parameterIndex);          // array index
                    instructions.Emit(OpCodes.Ldelem_Ref);         // now we have boxed out/ref value
                    var parameterElementType = parameter.ParameterType.GetElementType();
                    if (parameterElementType.HasGenericParameters) // a generic type requires the correct inner type
                    {
                        var referenceParameterType = (ByReferenceType)parameter.ParameterType;
                        parameterElementType = (GenericInstanceType)referenceParameterType.ElementType;
                    }
                    instructions.EmitUnboxOrCastIfNecessary(parameterElementType);
                    instructions.EmitStind(parameterElementType); // result is stored in ref parameter
                }
            }

            // and return
            instructions.Emit(OpCodes.Ret);
        }
Exemplo n.º 2
0
        private MethodDef WriteDelegateProceeder(MethodDef innerMethod, string methodName, MethodParameters parametersList, ModuleDef module)
        {
            if (innerMethod == null)
            {
                return(null);
            }
            // currently, this is unsupported
            // (since I have no idea how it works)
            if (innerMethod.DeclaringType.HasGenericParameters || innerMethod.HasGenericParameters)
            {
                return(null);
            }

            var proceederMethodSignature = new MethodSig(CallingConvention.Default, 0, module.CorLibTypes.Object,
                                                         new TypeSig[] { module.CorLibTypes.Object, new SZArraySig(module.CorLibTypes.Object) });
            var proceederMethodAttributes = MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig;
            var proceederMethod           = new MethodDefUser(GetDelegateProceederName(methodName, innerMethod.DeclaringType),
                                                              proceederMethodSignature, proceederMethodAttributes);

            proceederMethod.Body = new CilBody();
            proceederMethod.GenericParameters.AddRange(innerMethod.GenericParameters.Select(p => p.Clone(innerMethod)));

            // object, object[] -> this, arguments
            var instructions = new Instructions(proceederMethod.Body.Instructions, module);

            var declaringType = innerMethod.DeclaringType.ToTypeSig();

            if (innerMethod.DeclaringType.HasGenericParameters)
            {
                var genericTypeArgs = new List <TypeSig>();
                for (int genericTypeParameterIndex = 0;
                     genericTypeParameterIndex < innerMethod.DeclaringType.GenericParameters.Count;
                     genericTypeParameterIndex++)
                {
                    genericTypeArgs.Add(new GenericVar(genericTypeParameterIndex, innerMethod.DeclaringType));
                }
                declaringType = new GenericInstSig((ClassOrValueTypeSig)innerMethod.DeclaringType.ToTypeSig(), genericTypeArgs);
                //instructions.Emit(OpCodes.Castclass, innerMethod.DeclaringType.ToTypeSig()); // arg.0 --> (target type) arg.0
            }

            if (!innerMethod.IsStatic)
            {
                instructions.Emit(OpCodes.Ldarg_0);
                if (declaringType.IsValueType)
                {
                    instructions.Emit(OpCodes.Unbox, declaringType); // arg.0 --> (target type) arg.0
                }
                else
                {
                    instructions.Emit(OpCodes.Castclass, declaringType); // arg.0 --> (target type) arg.0
                }
            }
            //instructions.Emit(OpCodes.Ldnull);

            var localVariables = new Local[innerMethod.Parameters.Count];

            for (int parameterIndex = 0; parameterIndex < parametersList.Count; parameterIndex++)
            {
                var parameter = parametersList[parameterIndex];

                if (parameter.ParamDef == null)
                {
                    parameter.CreateParamDef();
                }

                var   parameterType = parameter.Type;
                Local local         = null;
                // the local type for references is the dereferenced type
                if (parameterType is ByRefSig)
                {
                    parameterType = parameterType.Next;
                    localVariables[parameterIndex] = local = new Local(parameterType);
                    proceederMethod.Body.Variables.Add(local);
                }

                // on pure out values we don't care
                if (!parameter.ParamDef.IsOut)
                {
                    instructions.Emit(OpCodes.Ldarg_1);    // arguments[]
                    instructions.EmitLdc(parameterIndex);  // index
                    instructions.Emit(OpCodes.Ldelem_Ref); // get array object
                    instructions.EmitUnboxOrCastIfNecessary(parameterType);

                    // when there is a local, use it (because we're going to pass the reference)
                    if (local != null)
                    {
                        instructions.EmitStloc(local);
                    }
                }
                // in all cases, if there is a local, it means we use it
                if (local != null)
                {
                    instructions.Emit(OpCodes.Ldloca_S, local);
                }
            }

            if (proceederMethod.HasGenericParameters)
            {
                var genericArgs = new List <TypeSig>();
                for (int genericParameterIndex = 0; genericParameterIndex < proceederMethod.GenericParameters.Count; genericParameterIndex++)
                {
                    genericArgs.Add(new GenericMVar(genericParameterIndex, innerMethod));
                }
                var genericInnerMethod = new MethodSpecUser(innerMethod, new GenericInstMethodSig(genericArgs));
                instructions.Emit(OpCodes.Call, genericInnerMethod);
            }
            else
            {
                instructions.Emit(OpCodes.Call, innerMethod);
            }

            // collect ref/output parameters, if any
            for (int parameterIndex = 0; parameterIndex < innerMethod.Parameters.Count; parameterIndex++)
            {
                // when there is a local variable, it was either a ref or an out, so we need to box it again to array
                var localVariable = localVariables[parameterIndex];
                if (localVariable == null)
                {
                    continue;
                }
                instructions.Emit(OpCodes.Ldarg_1);                  // array[...]
                instructions.EmitLdc(parameterIndex);                // index
                instructions.EmitLdloc(localVariable);               // result
                instructions.EmitBoxIfNecessary(localVariable.Type); // box
                instructions.Emit(OpCodes.Stelem_Ref);               // and store
            }

            if (!innerMethod.HasReturnType)
            {
                instructions.Emit(OpCodes.Ldnull);
            }
            else
            {
                instructions.EmitBoxIfNecessary(innerMethod.ReturnType);
            }

            instructions.Emit(OpCodes.Ret);

            innerMethod.DeclaringType.Methods.Add(proceederMethod);
            return(proceederMethod);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Writes the pointcut body.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <param name="innerMethod">The inner method.</param>
        /// <exception cref="System.InvalidOperationException">
        /// </exception>
        private void WritePointcutBody(MethodDefinition method, MethodDefinition innerMethod)
        {
            var moduleDefinition = method.Module;

            // now empty the old one and make it call the inner method...
            method.Body.InitLocals = true;
            method.Body.Instructions.Clear();
            method.Body.Variables.Clear();
            method.Body.ExceptionHandlers.Clear();
            var instructions = new Instructions(method.Body.Instructions, method.Module);

            var isStatic = method.Attributes.HasFlag(MethodAttributes.Static);

            // parameters
            var parametersVariable = new VariableDefinition("parameters", moduleDefinition.SafeImport(typeof(object[])));
            method.Body.Variables.Add(parametersVariable);

            instructions.EmitLdc(method.Parameters.Count);
            instructions.Emit(OpCodes.Newarr, moduleDefinition.SafeImport(typeof(object)));
            instructions.EmitStloc(parametersVariable);
            // setups parameters array
            for (int parameterIndex = 0; parameterIndex < method.Parameters.Count; parameterIndex++)
            {
                var parameter = method.Parameters[parameterIndex];
                // we don't care about output parameters
                if (!parameter.IsOut)
                {
                    instructions.EmitLdloc(parametersVariable); // array
                    instructions.EmitLdc(parameterIndex); // array index
                    instructions.EmitLdarg(parameter); // loads given parameter...
                    var parameterType = parameter.ParameterType;
                    if (parameter.ParameterType.IsByReference) // ...if ref, loads it as referenced value
                    {
                        parameterType = parameter.ParameterType.GetElementType();
                        instructions.EmitLdind(parameterType);
                    }
                    instructions.EmitBoxIfNecessary(parameterType); // ... and boxes it
                    instructions.Emit(OpCodes.Stelem_Ref);
                }
            }

            // if method has generic parameters, we also pass them to Proceed method
            VariableDefinition genericParametersVariable = null;
            // on static methods from genetic type, we also record the generic parameters type
            //var typeGenericParametersCount = isStatic ? method.DeclaringType.GenericParameters.Count : 0;
            var typeGenericParametersCount = method.DeclaringType.GenericParameters.Count;
            if (typeGenericParametersCount > 0 || method.HasGenericParameters)
            {
                //IL_0001: ldtoken !!T
                //IL_0006: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
                genericParametersVariable = new VariableDefinition("genericParameters", moduleDefinition.SafeImport(typeof(Type[])));
                method.Body.Variables.Add(genericParametersVariable);

                instructions.EmitLdc(typeGenericParametersCount + method.GenericParameters.Count);
                instructions.Emit(OpCodes.Newarr, moduleDefinition.SafeImport(typeof(Type)));
                instructions.EmitStloc(genericParametersVariable);

                var genericParameters = new List<GenericParameter>();
                for (int typeGenericParameterIndex = 0; typeGenericParameterIndex < typeGenericParametersCount; typeGenericParameterIndex++)
                    genericParameters.Add(method.DeclaringType.GenericParameters[typeGenericParameterIndex]);
                genericParameters.AddRange(method.GenericParameters);

                for (int genericParameterIndex = 0; genericParameterIndex < genericParameters.Count; genericParameterIndex++)
                {
                    instructions.EmitLdloc(genericParametersVariable); // array
                    instructions.EmitLdc(genericParameterIndex); // array index
                    instructions.Emit(OpCodes.Ldtoken, genericParameters[genericParameterIndex]);
                    // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
                    instructions.Emit(OpCodes.Call, ReflectionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(new RuntimeTypeHandle())));
                    instructions.Emit(OpCodes.Stelem_Ref);
                }
            }

            // null or instance
            instructions.Emit(isStatic ? OpCodes.Ldnull : OpCodes.Ldarg_0);
            // to fix peverify 0x80131854
            if (!isStatic && method.IsConstructor)
                instructions.Emit(OpCodes.Castclass, typeof(object));

            // parameters
            instructions.EmitLdloc(parametersVariable);

            // methods...
            // ... target
            // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
            instructions.Emit(OpCodes.Call, ReflectionUtility.GetMethodInfo(() => MethodBase.GetCurrentMethod()));

            // ... inner... If provided
            if (innerMethod != null)
            {
                // if type is generic, this is a bit more complex, because we need to pass the type
                if (method.DeclaringType.HasGenericParameters)
                {
                    // we want to reuse the MethodBase.GetCurrentMethod() result
                    // so it is stored into a variable, whose property DeclaringType is invoked later
                    var currentMethodVariable = new VariableDefinition("currentMethod", moduleDefinition.SafeImport(typeof(MethodBase)));
                    method.Body.Variables.Add(currentMethodVariable);
                    instructions.EmitStloc(currentMethodVariable);
                    instructions.EmitLdloc(currentMethodVariable);

                    instructions.Emit(OpCodes.Ldtoken, innerMethod);
                    instructions.EmitLdloc(currentMethodVariable);
                    instructions.Emit(OpCodes.Callvirt, ReflectionUtility.GetMethodInfo((Type t) => t.DeclaringType));
                    instructions.Emit(OpCodes.Callvirt, ReflectionUtility.GetMethodInfo((Type t) => t.TypeHandle));
                    // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
                    instructions.Emit(OpCodes.Call, ReflectionUtility.GetMethodInfo(() => MethodBase.GetMethodFromHandle(new RuntimeMethodHandle(), new RuntimeTypeHandle())));
                }
                else
                {
                    instructions.Emit(OpCodes.Ldtoken, innerMethod);
                    // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
                    instructions.Emit(OpCodes.Call, ReflectionUtility.GetMethodInfo(() => MethodBase.GetMethodFromHandle(new RuntimeMethodHandle())));
                }
            }
            else
                instructions.Emit(OpCodes.Ldnull);

            if (genericParametersVariable != null)
                instructions.EmitLdloc(genericParametersVariable);
            else
                instructions.Emit(OpCodes.Ldnull);

            // invoke the method
            var invocationType = TypeResolver.Resolve(moduleDefinition, Binding.InvocationTypeName, true);
            if (invocationType == null)
                throw new InvalidOperationException();
            var proceedMethodReference = invocationType.GetMethods().SingleOrDefault(m => m.IsStatic && m.Name == Binding.InvocationProceedAdviceMethodName);
            if (proceedMethodReference == null)
                throw new InvalidOperationException();
            var proceedMethod = moduleDefinition.SafeImport(proceedMethodReference);

            instructions.Emit(OpCodes.Call, proceedMethod);

            // get return value
            if (!method.ReturnType.SafeEquivalent(moduleDefinition.SafeImport(typeof(void))))
                instructions.EmitUnboxOrCastIfNecessary(method.ReturnType);
            else
                instructions.Emit(OpCodes.Pop); // if no return type, ignore Proceed() result

            // loads back out/ref parameters
            for (int parameterIndex = 0; parameterIndex < method.Parameters.Count; parameterIndex++)
            {
                var parameter = method.Parameters[parameterIndex];
                if (parameter.ParameterType.IsByReference)
                {
                    instructions.EmitLdarg(parameter); // loads given parameter (it is a ref)
                    instructions.EmitLdloc(parametersVariable); // array
                    instructions.EmitLdc(parameterIndex); // array index
                    instructions.Emit(OpCodes.Ldelem_Ref); // now we have boxed out/ref value
                    var parameterElementType = parameter.ParameterType.GetElementType();
                    instructions.EmitUnboxOrCastIfNecessary(parameterElementType);
                    instructions.EmitStind(parameterElementType); // result is stored in ref parameter
                }
            }

            // and return
            instructions.Emit(OpCodes.Ret);
        }