/// <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); }
/// <summary> /// Weaves the info advices for the given type. /// </summary> /// <param name="infoAdvisedType">Type of the module.</param> /// <param name="moduleDefinition">The module definition.</param> /// <param name="useWholeAssembly">if set to <c>true</c> [use whole assembly].</param> private void WeaveInfoAdvices(TypeDefinition infoAdvisedType, ModuleDefinition moduleDefinition, bool useWholeAssembly) { var invocationType = TypeResolver.Resolve(moduleDefinition, Binding.InvocationTypeName, true); if (invocationType == null) return; var proceedRuntimeInitializersReference = (from m in invocationType.GetMethods() where m.IsStatic && m.Name == Binding.InvocationProcessInfoAdvicesMethodName let parameters = m.Parameters where parameters.Count == 1 && parameters[0].ParameterType.SafeEquivalent( moduleDefinition.SafeImport(useWholeAssembly ? typeof(Assembly) : typeof(Type))) select m).SingleOrDefault(); if (proceedRuntimeInitializersReference == null) { Logger.WriteWarning("Info advice method not found"); return; } const string cctorMethodName = ".cctor"; var staticCtor = infoAdvisedType.Methods.SingleOrDefault(m => m.Name == cctorMethodName); if (staticCtor == null) { staticCtor = new MethodDefinition(cctorMethodName, (InjectAsPrivate ? MethodAttributes.Private : MethodAttributes.Public) | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, moduleDefinition.SafeImport(typeof(void))); infoAdvisedType.Methods.Add(staticCtor); } var instructions = new Instructions(staticCtor.Body.Instructions, staticCtor.Module); var proceedMethod = moduleDefinition.SafeImport(proceedRuntimeInitializersReference); if (useWholeAssembly) instructions.Emit(OpCodes.Call, moduleDefinition.SafeImport(ReflectionUtility.GetMethodInfo(() => Assembly.GetExecutingAssembly()))); else { instructions.Emit(OpCodes.Ldtoken, moduleDefinition.SafeImport(infoAdvisedType)); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed var getTypeFromHandleMethodInfo = ReflectionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(new RuntimeTypeHandle())); instructions.Emit(OpCodes.Call, moduleDefinition.SafeImport(getTypeFromHandleMethodInfo)); } instructions.Emit(OpCodes.Call, proceedMethod); instructions.Emit(OpCodes.Ret); }
/// <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) { var moduleDefinition = method.Module; // 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.ReturnType.SafeEquivalent(moduleDefinition.CorLibTypes.Void)) { 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] }); }
private IMethod CreateProceedMethod(IReadOnlyList <InvocationArgument> arguments, ModuleDef module, WeavingContext context) { // get the class from shortcuts var shortcutType = context.ShortcutClass; if (shortcutType == null) { shortcutType = new TypeDefUser(ShortcutTypeNamespace, ShortcutTypeName) { BaseType = module.Import(module.CorLibTypes.Object).ToTypeDefOrRef(), // Abstract + Sealed is Static class Attributes = TypeAttributes.NotPublic | TypeAttributes.Class | TypeAttributes.Abstract | TypeAttributes.Sealed }; module.Types.Add(shortcutType); context.ShortcutClass = shortcutType; } // create the method var nameBuilder = new StringBuilder("ProceedAspect"); var argumentIndex = 0; var methodSig = new MethodSig { RetType = module.CorLibTypes.Object, HasThis = false }; var defaultProceedMethod = GetDefaultProceedMethod(module, context); foreach (var argument in arguments) { if (argument.HasValue) { methodSig.Params.Add(defaultProceedMethod.MethodSig.Params[argumentIndex]); } // One day if there are arguments collision risks (IE optional arguments with same type), overload name argumentIndex++; } var method = new MethodDefUser(nameBuilder.ToString(), methodSig) { Body = new CilBody(), Attributes = MethodAttributes.Public | MethodAttributes.Static }; shortcutType.Methods.Add(method); var instructions = new Instructions(method.Body.Instructions, module); // now, either get value from given arguments or from default argumentIndex = 0; var usedArgumentIndex = 0; foreach (var argument in arguments) { if (argument.HasValue) // a given argument { instructions.EmitLdarg(method.Parameters[usedArgumentIndex++]); } else { arguments[argumentIndex].EmitDefault(instructions); } argumentIndex++; } instructions.Emit(OpCodes.Tailcall); // because target method returns object and this method also returns an object instructions.Emit(OpCodes.Call, defaultProceedMethod); instructions.Emit(OpCodes.Ret); return(method); }
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.ReturnType.SafeEquivalent(module.CorLibTypes.Void)) { instructions.Emit(OpCodes.Ldnull); } else { instructions.EmitBoxIfNecessary(innerMethod.ReturnType); } instructions.Emit(OpCodes.Ret); innerMethod.DeclaringType.Methods.Add(proceederMethod); return(proceederMethod); }