/// <summary> /// Determines whether the specified type reference is aspect. /// </summary> /// <param name="typeReference">The type reference.</param> /// <param name="markerInterface">The aspect marker interface.</param> /// <returns></returns> private bool IsMarker(ITypeDefOrRef typeReference, ITypeDefOrRef markerInterface) { lock (_isMarker) { var key = Tuple.Create(typeReference.FullName, markerInterface.FullName); // there is a cache, because the same attribute may be found several time // and we're in a hurry, the developper is waiting for his program to start! bool isMarker; if (_isMarker.TryGetValue(key, out isMarker)) { return(isMarker); } // otherwise look for type or implemented interfaces (recursively) var typeDef = TypeResolver.Resolve(typeReference); if (typeDef == null) { return(false); } var interfaces = typeDef.Interfaces; _isMarker[key] = isMarker = typeReference.SafeEquivalent(markerInterface) || interfaces.Any(i => IsMarker(i.Interface, markerInterface)); return(isMarker); } }
private TypeDef ResolveTypeOrGenericDefinition(ITypeDefOrRef typeDefOrRef) { var typeDef = typeDefOrRef as TypeDef; if (typeDef != null) { return(typeDef); } var typeRef = typeDefOrRef as TypeRef; if (typeRef != null) { return(TypeResolver.Resolve(typeRef)); } // tricky part here: assuming this a generic type var typeSpec = (TypeSpec)typeDefOrRef; var genericType = typeSpec.TryGetGenericInstSig(); if (genericType != null) { return(genericType.GenericType.TypeDef); } return(null); }
/// <summary> /// Determines whether the advice member is introduction, based on its type. /// </summary> /// <param name="adviceMemberTypeReference">The type reference.</param> /// <param name="introducedFieldType">Type of the introduced field.</param> /// <param name="isShared">if set to <c>true</c> the introduced field is shared among advices of the same type.</param> /// <param name="context">The context.</param> /// <returns> /// <c>true</c> if the specified advice member type reference is introduction; otherwise, <c>false</c>. /// </returns> private bool IsIntroduction(ITypeDefOrRef adviceMemberTypeReference, out ITypeDefOrRef introducedFieldType, out bool isShared, WeavingContext context) { introducedFieldType = null; isShared = false; var genericAdviceMemberTypeReference = adviceMemberTypeReference.TryGetGenericInstSig(); if (genericAdviceMemberTypeReference is null) { return(false); } var genericAdviceMemberTypeDefinition = TypeResolver.Resolve(genericAdviceMemberTypeReference.GenericType.TypeDefOrRef); if (genericAdviceMemberTypeDefinition is null) // in DEBUG or bogus cases, this may not be resolved. Whatever, this is not our field { return(false); } if (!genericAdviceMemberTypeDefinition.ImplementsType(context.IntroducedFieldType, TypeResolver)) { return(false); } introducedFieldType = genericAdviceMemberTypeReference.GenericArguments[0].ToTypeDefOrRef(); isShared = genericAdviceMemberTypeDefinition.ImplementsType(context.SharedIntroducedFieldType, TypeResolver); return(true); }
/// <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(TypeDef infoAdvisedType, ModuleDef moduleDefinition, bool useWholeAssembly) { var invocationType = TypeResolver.Resolve(moduleDefinition, typeof(Invocation)); if (invocationType == null) { return; } var proceedRuntimeInitializersReference = (from m in invocationType.Methods where m.IsStatic && m.Name == nameof(Invocation.ProcessInfoAdvices) let parameters = m.Parameters where parameters.Count == 1 && parameters[0].Type.SafeEquivalent( moduleDefinition.SafeImport(useWholeAssembly ? typeof(Assembly) : typeof(Type)).ToTypeSig()) select m).SingleOrDefault(); if (proceedRuntimeInitializersReference == null) { Logging.WriteWarning("Info advice method not found"); return; } // the cctor needs to be called after all initialization (in case some info advices collect data) infoAdvisedType.Attributes &= ~TypeAttributes.BeforeFieldInit; const string cctorMethodName = ".cctor"; var staticCtor = infoAdvisedType.Methods.SingleOrDefault(m => m.Name == cctorMethodName); var newStaticCtor = staticCtor == null; if (newStaticCtor) { staticCtor = new MethodDefUser(cctorMethodName, MethodSig.CreateStatic(moduleDefinition.CorLibTypes.Void), (InjectAsPrivate ? MethodAttributes.Private : MethodAttributes.Public) | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); staticCtor.Body = new CilBody(); 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, TypeImporter.Import(moduleDefinition, infoAdvisedType.ToTypeSig())); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed var getTypeFromHandleMethodInfo = ReflectionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(new RuntimeTypeHandle())); instructions.Emit(OpCodes.Call, moduleDefinition.SafeImport(getTypeFromHandleMethodInfo)); } instructions.Emit(OpCodes.Call, proceedMethod); // ret is only emitted if the method is new if (newStaticCtor) { instructions.Emit(OpCodes.Ret); } }
/// <summary> /// Creates the pointcut rules for a given advice. /// </summary> /// <param name="adviceType">Type of the advice.</param> /// <param name="context">The context.</param> /// <returns></returns> private PointcutSelector CreatePointcutSelector(ITypeDefOrRef adviceType, WeavingContext context) { var adviceTypeDef = TypeResolver.Resolve(adviceType); var rules = new PointcutSelector(); foreach (var customAttribute in adviceTypeDef.CustomAttributes) { rules += CreatePointcutSelector(customAttribute, context); } return(rules); }
/// <summary> /// Gets all attributes that implement the given advice interface /// </summary> /// <param name="reflectionNode">The reflection node.</param> /// <param name="markerInterface">The advice interface.</param> /// <param name="context">The context.</param> /// <returns></returns> private IEnumerable <MarkerDefinition> GetAllMarkers(ReflectionNode reflectionNode, ITypeDefOrRef markerInterface, WeavingContext context) { var markers = reflectionNode.GetAncestorsToChildren() .SelectMany(n => n.CustomAttributes .Where(a => !a.AttributeType.DefinitionAssembly.IsSystem()) .SelectMany(a => TypeResolver.Resolve(a.AttributeType).GetSelfAndParents()) .Where(t => IsMarker(t, markerInterface))) .Distinct() .Select(t => GetMarkerDefinition(t, context)); return(markers); }
private IEnumerable <TypeDef> GetDynamicHandledInterfaces(ModuleDef moduleDefinition) { var dynamicHandleAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(DynamicHandleAttribute)); foreach (var interfaceDefinition in moduleDefinition.Types.Where(t => t.IsInterface)) { if (interfaceDefinition.CustomAttributes.Any(c => c.AttributeType.SafeEquivalent(dynamicHandleAttributeType))) { yield return(interfaceDefinition); } } }
/// <summary> /// Gets the advice handled interfaces. /// This is done by analyzing calls in all methods from module /// </summary> /// <param name="moduleDefinition">The module definition.</param> /// <returns></returns> private IEnumerable <TypeReference> GetAdviceHandledInterfaces(ModuleDefinition moduleDefinition) { // the first method to look for in the final AdviceExtensions.Handle<>() method var adviceExtensionsType = TypeResolver.Resolve(moduleDefinition, typeof(AdviceExtensions)); var adviceHandleMethod = adviceExtensionsType.GetMethods().Single(m => m.IsPublic && m.HasGenericParameters && m.Name == nameof(AdviceExtensions.Handle)); var methodsSearched = new HashSet <MethodReference>(new MethodReferenceComparer()) { adviceHandleMethod }; var foundHandledInterfaces = new HashSet <TypeReference>(new TypeReferenceComparer()); var methodsToSearch = new List <Tuple <MethodDefinition, int> > { Tuple.Create(adviceHandleMethod, 0) }; while (methodsToSearch.Count > 0) { var methodToSearch = methodsToSearch[0]; methodsToSearch.RemoveAt(0); foreach (var t in GetAdviceHandledInterfaces(moduleDefinition, methodToSearch.Item1, methodToSearch.Item2)) { // if the supposed interface type itself is a generic parameter // this means that the calling method (Item2) is itself a generic parameter // and we have to lookup for calls to this method if (t.Item1.IsGenericParameter) { if (!methodsSearched.Contains(t.Item2)) { // ReSharper disable once AccessToForEachVariableInClosure var parameterIndex = t.Item2.GenericParameters.IndexOf(p => p.Name == t.Item1.Name); methodsSearched.Add(t.Item2); methodsToSearch.Add(Tuple.Create(t.Item2, parameterIndex)); Logger.WriteDebug("Now looking for references to '{0} [{1}]'", methodToSearch, parameterIndex); } } // only interfaces are processed by now else if (t.Item1.Resolve().IsInterface) { // otherwise, this is a direct call, keep the injected interface name if (!foundHandledInterfaces.Contains(t.Item1)) { foundHandledInterfaces.Add(t.Item1); yield return(t.Item1); } } } } }
private WeavingContext CreateWeavingContext(ModuleDefMD moduleDefinition) { var context = new WeavingContext { CompilerGeneratedAttributeType = moduleDefinition.Import(typeof(CompilerGeneratedAttribute)), PriorityAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(PriorityAttribute)), AbstractTargetAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(AbstractTargetAttribute)), AdviceInterfaceType = TypeResolver.Resolve(moduleDefinition, typeof(IAdvice)), WeavingAdviceInterfaceType = TypeResolver.Resolve(moduleDefinition, typeof(IWeavingAdvice)), ExecutionPointAttributeDefaultCtor = moduleDefinition.Import(TypeResolver.Resolve(moduleDefinition, typeof(ExecutionPointAttribute)).FindDefaultConstructor()), ExcludePointcutAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(ExcludePointcutAttribute)), IncludePointcutAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(IncludePointcutAttribute)), ExcludeAdviceAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(ExcludeAdvicesAttribute)), }; return(context); }
private IMethod GetDefaultProceedMethod(ModuleDef module, WeavingContext context) { if (context.InvocationProceedMethod == null) { var invocationType = TypeResolver.Resolve(module, typeof(Invocation)); if (invocationType == null) { throw new InvalidOperationException(); } var proceedMethodReference = invocationType.Methods.SingleOrDefault(m => m.IsStatic && m.Name == nameof(Invocation.ProceedAdvice2)); if (proceedMethodReference == null) { throw new InvalidOperationException(); } context.InvocationProceedMethod = module.SafeImport(proceedMethodReference); } return(context.InvocationProceedMethod); }
private WeavingContext CreateWeavingContext(ModuleDef moduleDefinition) { var context = new WeavingContext { CompilerGeneratedAttributeType = moduleDefinition.Import(typeof(CompilerGeneratedAttribute)), PriorityAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(PriorityAttribute)), AbstractTargetAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(AbstractTargetAttribute)), AdviceInterfaceType = TypeResolver.Resolve(moduleDefinition, typeof(IAdvice)), WeavingAdviceInterfaceType = TypeResolver.Resolve(moduleDefinition, typeof(IWeavingAdvice)), ExecutionPointAttributeDefaultCtor = moduleDefinition.Import(TypeResolver.Resolve(moduleDefinition, typeof(ExecutionPointAttribute))?.FindDefaultConstructor()), ExcludePointcutAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(ExcludePointcutAttribute)), IncludePointcutAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(IncludePointcutAttribute)), ExcludeAdviceAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(ExcludeAdvicesAttribute)), IntroducedFieldType = TypeResolver.Resolve(moduleDefinition, typeof(IntroducedField <>)), SharedIntroducedFieldType = TypeResolver.Resolve(moduleDefinition, typeof(SharedIntroducedField <>)), #pragma warning disable 618 IntroducedFieldsType = TypeResolver.Resolve(moduleDefinition, typeof(IntroducedFieldsRegistry)), #pragma warning restore 618 }; if (context.AdviceInterfaceType is not null) { if (context.ExecutionPointAttributeDefaultCtor is null) { Logging.WriteError("ExecutionPointAttribute default ctor was not found"); } if (context.ExcludePointcutAttributeType is null) { Logging.WriteError("ExcludePointcutAttributeType was not found"); } if (context.IncludePointcutAttributeType is null) { Logging.WriteError("IncludePointcutAttributeType was not found"); } if (context.ExcludeAdviceAttributeType is null) { Logging.WriteError("ExcludeAdviceAttributeType was not found"); } } return(context); }
/// <summary> /// Weaves the introductions. /// Introduces members as requested by aspects /// </summary> /// <param name="method">The method.</param> /// <param name="moduleDefinition">The module definition.</param> /// <param name="context">The context.</param> private void WeaveIntroductions(MethodDef method, ModuleDef moduleDefinition, WeavingContext context) { var typeDefinition = method.DeclaringType; var advices = GetAllMarkers(new MethodReflectionNode(method, null), context.AdviceInterfaceType, context); var markerAttributeCtor = moduleDefinition.SafeImport(TypeResolver.Resolve(moduleDefinition, typeof(IntroducedFieldAttribute)).FindConstructors().Single()); var markerAttributeCtorDef = new MemberRefUser(markerAttributeCtor.Module, markerAttributeCtor.Name, markerAttributeCtor.MethodSig, markerAttributeCtor.DeclaringType); foreach (var advice in advices) { var adviceDefinition = advice.Type; foreach (var field in adviceDefinition.Fields.Where(f => f.IsPublic)) { IntroduceMember(method.Module, field.Name, field.FieldType.ToTypeDefOrRef(), field.IsStatic, advice.Type, typeDefinition, markerAttributeCtorDef); } foreach (var property in adviceDefinition.Properties.Where(p => p.HasAnyPublic())) { IntroduceMember(method.Module, property.Name, property.PropertySig.RetType.ToTypeDefOrRef(), !property.PropertySig.HasThis, advice.Type, typeDefinition, markerAttributeCtorDef); } } }
/// <summary> /// Weaves the introductions. /// Introduces members as requested by aspects /// </summary> /// <param name="method">The method.</param> /// <param name="adviceInterface">The advice interface.</param> /// <param name="moduleDefinition">The module definition.</param> /// <param name="types">The types.</param> private void WeaveIntroductions(MethodDefinition method, TypeDefinition adviceInterface, ModuleDefinition moduleDefinition, Types types) { var typeDefinition = method.DeclaringType; var advices = GetAllMarkers(new MethodReflectionNode(method), adviceInterface, types); var markerAttributeCtor = moduleDefinition.SafeImport(TypeResolver.Resolve(moduleDefinition, typeof(IntroducedFieldAttribute)) .GetConstructors().Single()); foreach (var advice in advices) { var adviceDefinition = advice.Type.Resolve(); foreach (var field in adviceDefinition.Fields.Where(f => f.IsPublic)) { IntroduceMember(method.Module, field.Name, field.FieldType, field.IsStatic, advice.Type, typeDefinition, markerAttributeCtor); } foreach (var property in adviceDefinition.Properties.Where(p => p.HasAnyPublic())) { IntroduceMember(method.Module, property.Name, property.PropertyType, !property.HasThis, advice.Type, typeDefinition, markerAttributeCtor); } } }
/// <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(TypeDef infoAdvisedType, ModuleDef moduleDefinition, bool useWholeAssembly) { var invocationType = TypeResolver.Resolve(moduleDefinition, typeof(Invocation)); if (invocationType == null) { return; } var proceedRuntimeInitializersReference = (from m in invocationType.Methods where m.IsStatic && m.Name == nameof(Invocation.ProcessInfoAdvices) let parameters = m.Parameters where parameters.Count == 1 && parameters[0].Type.SafeEquivalent( moduleDefinition.SafeImport(useWholeAssembly ? typeof(Assembly) : typeof(Type)).ToTypeSig()) select m).SingleOrDefault(); if (proceedRuntimeInitializersReference == null) { Logging.WriteWarning("Info advice method not found"); return; } var instructions = GetCctorInstructions(infoAdvisedType); var proceedMethod = moduleDefinition.SafeImport(proceedRuntimeInitializersReference); if (useWholeAssembly) { instructions.Emit(OpCodes.Call, moduleDefinition.SafeImport(ReflectionUtility.GetMethodInfo(() => Assembly.GetExecutingAssembly()))); } else { instructions.Emit(OpCodes.Ldtoken, TypeImporter.Import(moduleDefinition, infoAdvisedType.ToTypeSig())); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed var getTypeFromHandleMethodInfo = ReflectionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(new RuntimeTypeHandle())); instructions.Emit(OpCodes.Call, moduleDefinition.SafeImport(getTypeFromHandleMethodInfo)); } instructions.Emit(OpCodes.Call, proceedMethod); }
private WeavingContext CreateWeavingContext(ModuleDef moduleDefinition) { var context = new WeavingContext { CompilerGeneratedAttributeType = moduleDefinition.Import(typeof(CompilerGeneratedAttribute)), PriorityAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(PriorityAttribute)), AbstractTargetAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(AbstractTargetAttribute)), AdviceInterfaceType = TypeResolver.Resolve(moduleDefinition, typeof(IAdvice)), WeavingAdviceInterfaceType = TypeResolver.Resolve(moduleDefinition, typeof(IWeavingAdvice)), ExecutionPointAttributeDefaultCtor = moduleDefinition.Import(TypeResolver.Resolve(moduleDefinition, typeof(ExecutionPointAttribute))?.FindDefaultConstructor()), ExcludePointcutAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(ExcludePointcutAttribute)), IncludePointcutAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(IncludePointcutAttribute)), ExcludeAdviceAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(ExcludeAdvicesAttribute)), }; if (context.AdviceInterfaceType != null) { if (context.ExecutionPointAttributeDefaultCtor == null) { Logging.WriteError("ExecutionPointAttribute default ctor was not found"); } if (context.ExcludePointcutAttributeType == null) { Logging.WriteError("ExcludePointcutAttributeType was not found"); } if (context.IncludePointcutAttributeType == null) { Logging.WriteError("IncludePointcutAttributeType was not found"); } if (context.ExcludeAdviceAttributeType == null) { Logging.WriteError("ExcludeAdviceAttributeType was not found"); } } return(context); }
/// <summary> /// Weaves the specified module definition. /// </summary> /// <param name="moduleDefinition">The module definition.</param> public bool Weave(ModuleDefMD moduleDefinition) { var auditTimer = new AuditTimer(); try { // sanity check auditTimer.NewZone("Types import"); // context var context = CreateWeavingContext(moduleDefinition); if (context.AdviceInterfaceType == null) { Logging.WriteWarning("IAdvice interface not found here (not referenced means not used), exiting"); return(false); } // runtime check auditTimer.NewZone("Runtime check"); var targetFramework = GetTargetFramework(moduleDefinition); InjectAsPrivate = targetFramework.Silverlight is null && targetFramework.WindowsPhone is null; // weave methods (they can be property-related, too) auditTimer.NewZone("Weavable methods detection"); Func <MarkedNode, bool> isWeavable = n => !IsFromComputerGeneratedType(n) && IsWeavable(n); var weavingAdvicesMethods = GetMarkedMethods(moduleDefinition, context.WeavingAdviceInterfaceType, context).Where(isWeavable).ToArray(); var weavableMethods = GetMarkedMethods(moduleDefinition, context.AdviceInterfaceType, context).Where(isWeavable).ToArray(); auditTimer.NewZone("Abstract targets"); var generatedFieldsToBeRemoved = new List <FieldDef>(); var methodsWithAbstractTarget = weavableMethods.Where(m => m.AbstractTarget).ToArray(); if (methodsWithAbstractTarget.Length > 0) { generatedFieldsToBeRemoved.AddRange(GetRemovableFields(methodsWithAbstractTarget, context)); foreach (var fieldReference in generatedFieldsToBeRemoved) { Logging.WriteDebug("Field {0} to be removed", fieldReference.FullName); } } auditTimer.NewZone("Methods weaving advice"); weavingAdvicesMethods.ForAll(i => RunWeavingAdvices(i, context)); auditTimer.NewZone("Methods weaving"); weavableMethods.ForAll(m => WeaveMethod(moduleDefinition, m, context)); auditTimer.NewZone("Weavable interfaces detection"); var weavableInterfaces = GetAdviceHandledInterfaces(moduleDefinition).Union(GetDynamicHandledInterfaces(moduleDefinition)).ToArray(); auditTimer.NewZone("Interface methods weaving"); weavableInterfaces.ForAll(i => WeaveInterface(moduleDefinition, i, context)); // and then, the info advices auditTimer.NewZone("Info advices weaving"); var infoAdviceInterface = TypeResolver.Resolve(moduleDefinition, typeof(IInfoAdvice)); moduleDefinition.GetTypes().ForAll(t => WeaveInfoAdvices(moduleDefinition, t, infoAdviceInterface, context)); auditTimer.NewZone("Abstract targets cleanup"); foreach (var generatedFieldToBeRemoved in generatedFieldsToBeRemoved) { generatedFieldToBeRemoved.DeclaringType.Fields.Remove(generatedFieldToBeRemoved); } auditTimer.LastZone(); var report = auditTimer.GetReport(); var maxLength = report.Keys.Max(k => k.Length); Logging.WriteDebug("--- Timings --------------------------"); foreach (var reportPart in report) { Logging.WriteDebug("{0} : {1}ms", reportPart.Key.PadRight(maxLength), (int)reportPart.Value.TotalMilliseconds); } Logging.WriteDebug("--------------------------------------"); var thisAssembly = GetType().Assembly; var thisVersion = thisAssembly.GetCustomAttribute <AssemblyVersionAttribute>()?.Version ?? thisAssembly.GetCustomAttribute <AssemblyInformationalVersionAttribute>()?.InformationalVersion; var thisTargetFramework = TargetFramework.FromLiteral(thisAssembly.GetCustomAttribute <TargetFrameworkAttribute>()?.FrameworkName); Logging.Write("MrAdvice {3}/{4} weaved module '{0}' (targeting framework {2}) in {1}ms", moduleDefinition.Assembly.FullName, (int)report.Sum(r => r.Value.TotalMilliseconds), targetFramework.ToString(), thisVersion, thisTargetFramework); return(true); } catch (Exception e) { Logging.WriteError("Internal error during {0}: {1}", auditTimer.CurrentZoneName, e); Logging.WriteError("Please complain, whine, cry, yell at https://github.com/ArxOne/MrAdvice/issues/new"); return(false); } }
/// <summary> /// Gets the advice handled interfaces. /// This is done by analyzing calls in all methods from module /// </summary> /// <param name="moduleDefinition">The module definition.</param> /// <returns></returns> private IEnumerable <TypeDef> GetAdviceHandledInterfaces(ModuleDef moduleDefinition) { // the first method to look for in the final AdviceExtensions.Handle<>() method var adviceExtensionsType = TypeResolver.Resolve(moduleDefinition, typeof(AdviceExtensions)); var adviceHandleMethod = adviceExtensionsType.Methods.Single(m => m.IsPublic && m.HasGenericParameters && m.Name == nameof(AdviceExtensions.Handle)); var methodsSearched = new HashSet <MethodDef>(new MethodReferenceComparer()) { adviceHandleMethod }; var foundHandledInterfaces = new HashSet <ITypeDefOrRef>(new TypeReferenceComparer()); var methodsToSearch = new List <Tuple <MethodDef, int> > { Tuple.Create(adviceHandleMethod, 0) }; while (methodsToSearch.Count > 0) { var methodToSearch = methodsToSearch[0]; methodsToSearch.RemoveAt(0); foreach (var t in GetAdviceHandledInterfaces(moduleDefinition, methodToSearch.Item1, methodToSearch.Item2)) { // if the supposed interface type itself is a generic parameter // this means that the calling method (Item2) is itself a generic parameter // and we have to lookup for calls to this method if (t.Item1.ContainsGenericParameter) { if (!methodsSearched.Contains(t.Item2)) { // ReSharper disable once AccessToForEachVariableInClosure var parameterIndex = t.Item2.GenericParameters.IndexOf(p => p.Name == t.Item1.TypeName); methodsSearched.Add(t.Item2); methodsToSearch.Add(Tuple.Create(t.Item2, parameterIndex)); Logging.WriteDebug("Now looking for references to '{0} [{1}]'", methodToSearch.Item1.ToString(), parameterIndex); } } // only interfaces are processed by now else { var interfaceDef = t.Item1 as TypeDef; if (interfaceDef is null) { if (t.Item1 is TypeRef interfaceRef) { interfaceDef = TypeResolver.Resolve(interfaceRef); } } if (interfaceDef is null) { Logging.WriteError("Can not identify {0} as valid weavable interface. If you feel this is unfair --> https://github.com/ArxOne/MrAdvice/issues/new", t.Item1.ToString()); continue; } if (interfaceDef.IsInterface) { // otherwise, this is a direct call, keep the injected interface name if (!foundHandledInterfaces.Contains(t.Item1)) { foundHandledInterfaces.Add(t.Item1); yield return(interfaceDef); } } else { Logging.WriteError("Only interfaces can be weaved with Handle<>() extension method and {0} it not an interface (but I’m glad you asked)", interfaceDef.FullName); } } } } }
/// <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); }
/// <summary> /// Weaves the specified module definition. /// </summary> /// <param name="moduleDefinition">The module definition.</param> public void Weave(ModuleDefinition moduleDefinition) { var auditTimer = new AuditTimer(); var stopwatch = new Stopwatch(); stopwatch.Start(); // sanity check auditTimer.NewZone("IAdvice location"); var adviceInterface = TypeResolver.Resolve(moduleDefinition, typeof(IAdvice)); if (adviceInterface == null) { Logger.WriteWarning("IAdvice interface not found here (not referenced means not used), exiting"); return; } // context var types = new Types { CompilerGeneratedAttributeType = moduleDefinition.Import(typeof(CompilerGeneratedAttribute)), PriorityAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(PriorityAttribute)), AbstractTargetAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(AbstractTargetAttribute)), WeavingAdviceAttributeType = TypeResolver.Resolve(moduleDefinition, typeof(IWeavingAdvice)) }; // runtime check auditTimer.NewZone("Runtime check"); var targetFramework = GetTargetFramework(moduleDefinition); InjectAsPrivate = targetFramework.Silverlight == null && targetFramework.WindowsPhone == null; //Logger.WriteDebug("t1: {0}ms", (int)stopwatch.ElapsedMilliseconds); // weave methods (they can be property-related, too) auditTimer.NewZone("Weavable methods detection"); var weavableMethods = GetMarkedMethods(moduleDefinition, adviceInterface, types).Where(IsWeavable).ToArray(); auditTimer.NewZone("Abstract targets"); var generatedFieldsToBeRemoved = new List <FieldReference>(); var methodsWithAbstractTarget = weavableMethods.Where(m => m.AbstractTarget).ToArray(); if (methodsWithAbstractTarget.Length > 0) { generatedFieldsToBeRemoved.AddRange(GetRemovableFields(methodsWithAbstractTarget, types)); foreach (var fieldReference in generatedFieldsToBeRemoved) { Logger.WriteDebug("Field {0} to be removed", fieldReference.FullName); } } auditTimer.NewZone("Methods weaving"); weavableMethods.AsParallel().ForAll(m => WeaveMethod(moduleDefinition, m, adviceInterface, types)); auditTimer.NewZone("Weavable interfaces detection"); var weavableInterfaces = GetAdviceHandledInterfaces(moduleDefinition).ToArray(); auditTimer.NewZone("Interface methods weaving"); weavableInterfaces.AsParallel().ForAll(i => WeaveInterface(moduleDefinition, i)); //Logger.WriteDebug("t2: {0}ms", (int)stopwatch.ElapsedMilliseconds); // and then, the info advices auditTimer.NewZone("Info advices weaving"); var infoAdviceInterface = TypeResolver.Resolve(moduleDefinition, typeof(IInfoAdvice)); moduleDefinition.GetTypes() .AsParallel() .ForAll(t => WeaveInfoAdvices(moduleDefinition, t, infoAdviceInterface, types)); auditTimer.NewZone("Abstract targets cleanup"); foreach (var generatedFieldToBeRemoved in generatedFieldsToBeRemoved) { generatedFieldToBeRemoved.DeclaringType.Resolve().Fields.Remove(generatedFieldToBeRemoved.Resolve()); } auditTimer.LastZone(); //Logger.WriteDebug("t3: {0}ms", (int)stopwatch.ElapsedMilliseconds); var report = auditTimer.GetReport(); var maxLength = report.Keys.Max(k => k.Length); Logger.WriteDebug("--- Timings --------------------------"); foreach (var reportPart in report) { Logger.WriteDebug("{0} : {1}ms", reportPart.Key.PadRight(maxLength), (int)reportPart.Value.TotalMilliseconds); } Logger.WriteDebug("--------------------------------------"); Logger.Write("MrAdvice {3} weaved module '{0}' (targeting framework {2}) in {1}ms", moduleDefinition.Assembly.FullName, (int)stopwatch.ElapsedMilliseconds, targetFramework, Product.Version); }
/// <summary> /// Weaves the interface. /// What we do here is: /// - creating a class (which is named after the interface name) /// - this class implements all interface members /// - all members invoke Invocation.ProcessInterfaceMethod /// </summary> /// <param name="moduleDefinition">The module definition.</param> /// <param name="interfaceType">Type of the interface.</param> /// <param name="context">The context.</param> private void WeaveInterface(ModuleDefMD moduleDefinition, TypeDef interfaceType, WeavingContext context) { var importedInterfaceType = moduleDefinition.Import(interfaceType); Logging.WriteDebug("Weaving interface '{0}'", interfaceType.FullName); TypeDef implementationType; TypeDef advisedInterfaceType; lock (moduleDefinition) { // ensure we're creating the interface only once var implementationTypeName = GetImplementationTypeName(interfaceType.Name); var implementationTypeNamespace = interfaceType.Namespace; if (moduleDefinition.GetTypes().Any(t => t.Namespace == implementationTypeNamespace && t.Name == implementationTypeName)) { return; } // now, create the implementation type var typeAttributes = (InjectAsPrivate ? TypeAttributes.NotPublic : TypeAttributes.Public) | TypeAttributes.Class | TypeAttributes.BeforeFieldInit; advisedInterfaceType = TypeResolver.Resolve(moduleDefinition, typeof(AdvisedInterface)); // TODO: this should work using TypeImporter.Import var advisedInterfaceTypeReference = moduleDefinition.Import(advisedInterfaceType); implementationType = new TypeDefUser(implementationTypeNamespace, implementationTypeName, advisedInterfaceTypeReference) { Attributes = typeAttributes }; implementationType.Interfaces.Add(new InterfaceImplUser(importedInterfaceType)); lock (moduleDefinition) moduleDefinition.Types.Add(implementationType); } // create empty .ctor. This .NET mofo wants it! var baseEmptyConstructor = moduleDefinition.SafeImport(advisedInterfaceType.FindConstructors().Single()); const MethodAttributes ctorAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; var method = new MethodDefUser(".ctor", baseEmptyConstructor.MethodSig, ctorAttributes); method.Body = new CilBody(); method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, baseEmptyConstructor)); method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); implementationType.Methods.Add(method); // create implementation methods foreach (var currentInterfaceType in interfaceType.GetAllInterfaces(TypeResolver)) { foreach (var interfaceMethod in currentInterfaceType.Methods.Where(m => !m.IsSpecialName)) { WeaveInterfaceMethod(interfaceMethod, implementationType, true, context); } // create implementation properties foreach (var interfaceProperty in currentInterfaceType.Properties) { var implementationProperty = new PropertyDefUser(interfaceProperty.Name, interfaceProperty.PropertySig); implementationType.Properties.Add(implementationProperty); if (interfaceProperty.GetMethod != null) { implementationProperty.GetMethod = WeaveInterfaceMethod(interfaceProperty.GetMethod, implementationType, InjectAsPrivate, context); } if (interfaceProperty.SetMethod != null) { implementationProperty.SetMethod = WeaveInterfaceMethod(interfaceProperty.SetMethod, implementationType, InjectAsPrivate, context); } } // create implementation events foreach (var interfaceEvent in currentInterfaceType.Events) { var implementationEvent = new EventDefUser(interfaceEvent.Name, interfaceEvent.EventType); implementationType.Events.Add(implementationEvent); if (interfaceEvent.AddMethod != null) { implementationEvent.AddMethod = WeaveInterfaceMethod(interfaceEvent.AddMethod, implementationType, InjectAsPrivate, context); } if (interfaceEvent.RemoveMethod != null) { implementationEvent.RemoveMethod = WeaveInterfaceMethod(interfaceEvent.RemoveMethod, implementationType, InjectAsPrivate, context); } } } }
/// <summary> /// Weaves the interface. /// What we do here is: /// - creating a class (wich is named after the interface name) /// - this class implements all interface members /// - all members invoke Invocation.ProcessInterfaceMethod /// </summary> /// <param name="moduleDefinition">The module definition.</param> /// <param name="interfaceType">Type of the interface.</param> private void WeaveInterface(ModuleDefinition moduleDefinition, TypeReference interfaceType) { Logger.WriteDebug("Weaving interface '{0}'", interfaceType.FullName); TypeDefinition implementationType; TypeDefinition advisedInterfaceType; TypeDefinition interfaceTypeDefinition; lock (moduleDefinition) { // ensure we're creating the interface only once var implementationTypeName = GetImplementationTypeName(interfaceType.Name); var implementationTypeNamespace = interfaceType.Namespace; if (moduleDefinition.GetTypes().Any(t => t.Namespace == implementationTypeNamespace && t.Name == implementationTypeName)) { return; } // now, create the implementation type interfaceTypeDefinition = interfaceType.Resolve(); var typeAttributes = (InjectAsPrivate ? TypeAttributes.NotPublic : TypeAttributes.Public) | TypeAttributes.Class | TypeAttributes.BeforeFieldInit; advisedInterfaceType = TypeResolver.Resolve(moduleDefinition, typeof(AdvisedInterface)); var advisedInterfaceTypeReference = moduleDefinition.SafeImport(advisedInterfaceType); implementationType = new TypeDefinition(implementationTypeNamespace, implementationTypeName, typeAttributes, advisedInterfaceTypeReference); lock (moduleDefinition) moduleDefinition.Types.Add(implementationType); } implementationType.Interfaces.Add(interfaceType); // create empty .ctor. This .NET mofo wants it! var baseEmptyConstructor = moduleDefinition.SafeImport(advisedInterfaceType.Resolve().GetConstructors().Single()); const MethodAttributes ctorAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; var method = new MethodDefinition(".ctor", ctorAttributes, moduleDefinition.TypeSystem.Void); method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, baseEmptyConstructor)); method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); implementationType.Methods.Add(method); // create implementation methods foreach (var interfaceMethod in interfaceTypeDefinition.GetMethods().Where(m => !m.IsSpecialName)) { WeaveInterfaceMethod(interfaceMethod, implementationType, true); } // create implementation properties foreach (var interfaceProperty in interfaceTypeDefinition.Properties) { var implementationProperty = new PropertyDefinition(interfaceProperty.Name, PropertyAttributes.None, interfaceProperty.PropertyType); implementationType.Properties.Add(implementationProperty); if (interfaceProperty.GetMethod != null) { implementationProperty.GetMethod = WeaveInterfaceMethod(interfaceProperty.GetMethod, implementationType, InjectAsPrivate); } if (interfaceProperty.SetMethod != null) { implementationProperty.SetMethod = WeaveInterfaceMethod(interfaceProperty.SetMethod, implementationType, InjectAsPrivate); } } // create implementation events foreach (var interfaceEvent in interfaceTypeDefinition.Events) { var implementationEvent = new EventDefinition(interfaceEvent.Name, EventAttributes.None, moduleDefinition.SafeImport(interfaceEvent.EventType)); implementationType.Events.Add(implementationEvent); if (interfaceEvent.AddMethod != null) { implementationEvent.AddMethod = WeaveInterfaceMethod(interfaceEvent.AddMethod, implementationType, InjectAsPrivate); } if (interfaceEvent.RemoveMethod != null) { implementationEvent.RemoveMethod = WeaveInterfaceMethod(interfaceEvent.RemoveMethod, implementationType, InjectAsPrivate); } } }