/// <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> /// <param name="context">The context.</param> /// <exception cref="System.InvalidOperationException"></exception> private void WritePointcutBody(MethodDef method, MethodDef innerMethod, bool abstractedTarget, WeavingContext context) { // now empty the old one and make it call the inner method... if (method.Body == null) { method.Body = new CilBody(); } 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 targetArgument = GetTargetArgument(method); var parametersArgument = GetParametersArgument(method, out var parametersVariable); var methodArgument = GetMethodArgument(method); var innerMethodArgument = GetInnerMethodArgument(innerMethod); var typeArgument = GetTypeArgument(method); var abstractedArgument = GetAbstractedArgument(abstractedTarget); var genericParametersArgument = GetGenericParametersArgument(method); var innerMethodDelegateArgument = GetInnerMethodDelegateArgument(innerMethod, method); WriteProceedCall(instructions, context, targetArgument, parametersArgument, methodArgument, innerMethodArgument, innerMethodDelegateArgument, typeArgument, abstractedArgument, genericParametersArgument); // get return value if (method.HasReturnType) { instructions.EmitUnboxOrCastIfNecessary(method.ReturnType); } else { instructions.Emit(OpCodes.Pop); // if no return type, ignore Proceed() result } // loads back out/ref parameters var methodParameters = new MethodParameters(method); for (int parameterIndex = 0; parameterIndex < methodParameters.Count; parameterIndex++) { var parameter = methodParameters[parameterIndex]; if (parameter.Type is ByRefSig) { 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.Type.Next; instructions.EmitUnboxOrCastIfNecessary(parameterElementType); instructions.EmitStind(parameterElementType); // result is stored in ref parameter } } // and return instructions.Emit(OpCodes.Ret); method.Body.PdbMethod = new PdbMethod { Scope = new PdbScope { Start = method.Body.Instructions[0] } }; method.Body.PdbMethod.Scope.Scopes.Add(new PdbScope { Start = method.Body.Instructions[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); }
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); }
/// <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); }