/// <summary> /// Create an adhoc variable used to retain an instance of an attribute. /// </summary> /// <param name="method">The method weaver.</param> /// <param name="attribute">The attribute.</param> /// <param name="member">The member.</param> /// <returns></returns> public Variable CreateAttributeAdhoc(MethodEmitter method, CustomAttribute attribute, MemberReference member) { var type = attribute.AttributeType; var variable = method.EmitLocal(type); var il = method.GetIL(); foreach (var arg in attribute.ConstructorArguments) { CreateAttributeParameter(method, attribute, arg); } il.Emit(Codes.Create(attribute.Constructor)); il.Emit(Codes.Store(variable)); if (attribute.HasInterface(Context.Finder.IInstanceAware) && !method.Target.IsStatic) { il.Emit(Codes.Load(variable)); il.Emit(Codes.This); il.Emit(Codes.Invoke(Context.Finder.InstanceAwareInstanceSet)); } foreach (var prop in attribute.Properties) { CreateAttributeProperty(method, attribute, variable, prop); } CreateAttributeRequirements(il, attribute, member, variable, false); return(variable); }
/// <summary> /// Creates a property which replaces a provided field, and implements the <c>get</c> and <c>set</c> methods. /// </summary> /// <param name="emitter">The type emitter.</param> /// <param name="field">The field.</param> /// <returns></returns> public PropertyEmitter CreateFieldProperty(TypeEmitter emitter, FieldDefinition field) { var variable = new Variable(field); var property = emitter.EmitProperty(field.Name, field.FieldType, field.IsStatic); var context = emitter.Context; field.Name = $"<{field.Name}>k__BackingField"; context.AddCompilerGenerated(field); context.AddNonSerialized(field); var getter = property.GetGetter(); var gil = getter.GetIL(); gil.Emit(Codes.Nop); gil.Emit(Codes.ThisIf(variable)); gil.Emit(Codes.Load(variable)); gil.Emit(Codes.Return); getter.Body.InitLocals = true; var setter = property.GetSetter(); var sil = setter.GetIL(); sil.Emit(Codes.Nop); sil.Emit(Codes.ThisIf(variable)); sil.Emit(Codes.Arg(field.IsStatic ? 0 : 1)); sil.Emit(Codes.Store(variable)); sil.Emit(Codes.Return); setter.Body.InitLocals = true; return(property); }
/// <summary> /// Creates and populates any additional contract requirements of the attribute. /// </summary> /// <param name="il">The code emitter.</param> /// <param name="attribute">The attribute.</param> /// <param name="member">The member the attribute is associated with.</param> /// <param name="variable">The variable containing the attribute.</param> /// <param name="isStatic">Whether the attribute container is static.</param> public void CreateAttributeRequirements(CodeEmitter il, CustomAttribute attribute, MemberReference member, Variable variable, bool isStatic) { if (attribute.HasInterface(Context.Finder.IMemberAware) && member != null) { il.Emit(Codes.ThisIf(variable)); il.Emit(Codes.Load(variable)); if (member is MethodReference method) { var minfo = CreateMethodInfo(new MethodEmitter(il.Parent.Parent, method)); il.Emit(Codes.ThisIf(minfo)); il.Emit(Codes.Load(minfo)); } else if (member is PropertyReference property) { var pinfo = CreatePropertyInfo(new PropertyEmitter(il.Parent.Parent, property, isStatic)); il.Emit(Codes.ThisIf(pinfo)); il.Emit(Codes.Load(pinfo)); } il.Emit(Codes.Invoke(Context.Finder.MemberAwareMemberSet)); } if (attribute.HasInterface(Context.Finder.IRequireInitialization)) { il.Emit(Codes.ThisIf(variable)); il.Emit(Codes.Load(variable)); il.Emit(Codes.Invoke(Context.Finder.RequireInitializationInitialize)); } }
/// <summary> /// Copies the values of an array of parameter values into the corresponding parameters. /// </summary> /// <param name="method">The method.</param> /// <param name="array">The array.</param> public void CopyArgumentArrayToParameters(MethodEmitter method, Variable array) { var parameters = method.Target.Parameters; var count = parameters.Count; var il = method.GetIL(); for (int i = 0; i < count; i++) { var p = parameters[i]; var variable = new Variable(p); var type = p.ParameterType; if (type.IsByReference || p.IsOut) { continue; } il.Emit(Codes.Load(array)); il.Emit(Codes.Int(i)); il.Emit(Codes.LoadArray); il.Emit(Codes.Unbox(type)); il.Emit(Codes.Store(variable)); } CopyArgumentArrayToReferences(method, array); }
/// <summary> /// Weaves an implementation of a property against the provided type. /// </summary> /// <param name="emitter">The emitter.</param> /// <param name="field">The attribute field.</param> /// <param name="property">The property.</param> /// <param name="interfaceType">The type of the interface.</param> public void WeaveImplementedProperty(TypeEmitter emitter, Variable field, PropertyDefinition property, TypeDefinition interfaceType) { var emitted = emitter.EmitProperty(property.Name, property.PropertyType.Import(), toBackingField: true); var implemented = field.Type.GetProperty(property.Name, property.PropertyType)?.Resolve(); if (implemented == null) { implemented = field.Type.GetProperty($"{interfaceType.FullName}.{property.Name}", returnType: property.PropertyType)?.Resolve(); if (implemented == null) { throw new MissingMemberException($"Cannot implement '{field.Type.FullName}' as it does not implement property '{property.Name}'"); } } var source = new PropertyEmitter(emitter, implemented); if (source.HasGetter && !emitted.HasGetter) { var getter = emitted.GetGetter(); var il = getter.GetIL(); var propertyGet = property.GetMethod?.Import() ?? implemented.GetMethod.Import(); getter.Body.SimplifyMacros(); il.Emit(Codes.Nop); il.Emit(Codes.ThisIf(field)); il.Emit(Codes.Cast(interfaceType)); il.Emit(Codes.Load(field)); il.Emit(Codes.Invoke(propertyGet.GetGeneric())); il.Emit(Codes.Return); getter.Body.OptimizeMacros(); getter.Body.InitLocals = true; } if (source.HasSetter && !emitted.HasSetter) { var setter = emitted.GetSetter(); var il = setter.GetIL(); var propertySet = property.SetMethod?.Import() ?? implemented.SetMethod.Import(); setter.Body.SimplifyMacros(); il.Emit(Codes.Nop); il.Emit(Codes.ThisIf(field)); il.Emit(Codes.Load(field)); il.Emit(Codes.Arg(setter.Target.IsStatic ? 0 : 1)); il.Emit(Codes.Invoke(propertySet.GetGeneric())); il.Emit(Codes.Return); setter.Body.OptimizeMacros(); setter.Body.InitLocals = true; } }
/// <summary> /// Creates a reference to a parameter used in a custom attribute. /// </summary> /// <param name="emitter">The emitter.</param> /// <param name="attribute">The attribute.</param> /// <param name="arg">The argument.</param> public void CreateAttributeParameter(MethodEmitter emitter, CustomAttribute attribute, CustomAttributeArgument arg) { var il = emitter.GetIL(); if (arg.Value == null) { il.Emit(Codes.Null); return; } var type = arg.Type; if (type.IsArray) { var elements = (arg.Value as IEnumerable).Cast <CustomAttributeArgument>().ToArray(); il.Emit(Codes.Int(elements.Length)); il.Emit(Codes.CreateArray(type.GetElementType())); if (elements.Length == 0) { return; } il.Emit(Codes.Duplicate); for (int i = 0, count = elements.Length; i < count; i++) { il.Emit(Codes.Int(i)); if (elements[i].Value == null) { il.Emit(Codes.Null); il.Emit(Codes.StoreArray); } else { il.Emit(Codes.Load(elements[i].Value)); il.Emit(Codes.StoreArray); } if (i + 1 < count) { il.Emit(Codes.Duplicate); } } } else { il.Emit(Codes.Load(arg.Value)); } }
/// <summary> /// Creates an multi-instance-level field used to retain an instance of an attribute. /// </summary> /// <param name="emitter">The type weaver.</param> /// <param name="attribute">The attribute.</param> /// <param name="member">The member.</param> /// <returns></returns> public Variable CreateAttributeMultiInstanced(TypeEmitter emitter, CustomAttribute attribute, MemberReference member) { var index = Interlocked.Increment(ref id); var type = attribute.AttributeType; var name = $"<{type.Name}${index}>k__Attribute"; var existing = emitter.GetField(name, type, toStatic: false); if (existing != null) { return(existing); } var field = emitter.EmitField(name, type); foreach (var ctor in emitter.GetConstructors()) { var il = ctor.GetIL(); il.Insert = CodeInsertion.Before; il.Position = il.GetConstructorBaseOrThis()?.Next ?? il.GetFirst(); il.Emit(Codes.Nop); il.Emit(Codes.This); foreach (var arg in attribute.ConstructorArguments) { CreateAttributeParameter(ctor, attribute, arg); } il.Emit(Codes.Create(attribute.Constructor)); il.Emit(Codes.Store(field)); if (attribute.HasInterface(Context.Finder.IInstanceAware)) { il.Emit(Codes.ThisIf(field)); il.Emit(Codes.Load(field)); il.Emit(Codes.This); il.Emit(Codes.Invoke(Context.Finder.InstanceAwareInstanceSet)); } foreach (var prop in attribute.Properties) { CreateAttributeProperty(ctor, attribute, field, prop); } CreateAttributeRequirements(il, attribute, member, field, false); } return(field); }
/// <summary> /// Creates a reference to a property used in a custom attribute. /// </summary> /// <param name="emitter">The emitter.</param> /// <param name="attribute">The attribute.</param> /// <param name="field">The attribute variable.</param> /// <param name="property">The property.</param> public void CreateAttributeProperty(MethodEmitter emitter, CustomAttribute attribute, Variable field, Mono.Cecil.CustomAttributeNamedArgument property) { var il = emitter.GetIL(); var member = field.Type.Resolve().Properties.FirstOrDefault(p => p.Name == property.Name)?.SetMethod; if (member == null) { throw new MissingMemberException($"Cannot resolve property {property.Name} of {attribute.AttributeType.Name} as there is no setter"); } il.Emit(Codes.ThisIf(field)); il.Emit(Codes.Load(field)); CreateAttributeParameter(emitter, attribute, property.Argument); il.Emit(Codes.Invoke(member.Import())); }
/// <summary> /// Weaves the return instructions of the provided method to leave to a new instruction. /// </summary> /// <param name="weaver">The method weaver.</param> /// <param name="storage">The storage variable for any captured return value.</param> /// <param name="hasMethodInterceptors">Whether method interceptors are present which could change the return value.</param> /// <returns>The instruction now located at the tail of the method.</returns> public Instruction WeaveMethodReturnsRoute(MethodEmitter weaver, Variable storage, bool hasMethodInterceptors) { var il = weaver.Body.Instructions; var pos = weaver.GetIL().Position; var instruction = (Instruction)null; if (weaver.Target.IsReturn()) { instruction = hasMethodInterceptors ? Codes.Nop : Codes.Load(storage); il.Add(instruction); il.Add(Codes.Return); for (int i = 0; i < il.Count - 2; i++) { if (il[i].OpCode == OpCodes.Ret) { var store = Codes.Store(storage); il[i].OpCode = store.OpCode; il[i].Operand = store.Operand; weaver.Body.GetILProcessor().InsertAfter(il[i], Codes.Leave(instruction)); } } } else { instruction = Codes.Return; il.Add(instruction); for (int i = 0, count = il.Count - 1; i < count; i++) { var ix = il[i]; if (ix.OpCode == OpCodes.Ret) { ix.OpCode = OpCodes.Leave; ix.Operand = instruction; } } } return(instruction); }
/// <summary> /// Weave an implementation of a method against the provided type. /// </summary> /// <param name="emitter">The emitter.</param> /// <param name="field">The attribute field.</param> /// <param name="method">The method.</param> /// <param name="interfaceType">The type of the interface.</param> public void WeaveImplementedMethod(TypeEmitter emitter, Variable field, MethodReference method, TypeDefinition interfaceType) { var resolved = method.Resolve(); var emitted = emitter.EmitMethod( method.Name, method.ReturnType.Import(), parameterTypes: method.HasParameters ? method.Parameters.Select(p => p.ParameterType.Import()).ToArray() : new TypeReference[0], genericTypes: method.HasGenericParameters ? method.GenericParameters.ToArray() : new GenericParameter[0], toStatic: resolved.IsStatic, toVisibility: resolved.GetVisiblity() ); var parameters = method.Parameters.Select(p => p.ParameterType).ToArray(); var generics = method.GenericParameters.ToArray(); var implemented = field.Type.GetMethod(method.Name, returns: method.ReturnType, parameters: parameters, generics: generics); if (implemented == null) { implemented = field.Type.GetMethod($"{interfaceType.FullName}.{method.Name}", returns: method.ReturnType, parameters: parameters, generics: generics); if (implemented == null) { throw new MissingMemberException($"Cannot implement '{field.Type.FullName}' as it does not implement method '{method.FullName}'"); } } var il = emitted.GetIL(); il.Emit(Codes.Nop); il.Emit(Codes.ThisIf(field)); il.Emit(Codes.Load(field)); for (int i = 0, count = method.Parameters.Count; i < count; i++) { il.Emit(Codes.Arg(i + 1)); } il.Emit(Codes.Invoke(method.Import().GetGeneric())); il.Emit(Codes.Return); emitted.Body.InitLocals = true; }
/// <summary> /// Copies the values of an array of parameter values into the required references. /// </summary> /// <param name="method">The method.</param> /// <param name="array">The array.</param> public void CopyArgumentArrayToReferences(MethodEmitter method, Variable array) { var parameters = method.Target.Parameters; var count = parameters.Count; var il = method.GetIL(); for (int i = 0; i < count; i++) { var p = parameters[i]; var type = p.ParameterType; if (!type.IsByReference || p.IsOut) { continue; } var spec = (TypeSpecification)type; var unboxing = true; il.Emit(Codes.Arg(p)); il.Emit(Codes.Load(array)); il.Emit(Codes.Int(i)); il.Emit(Codes.LoadArray); var code = OpCodes.Nop; switch (spec.ElementType.MetadataType) { case MetadataType.Boolean: case MetadataType.SByte: case MetadataType.Byte: code = OpCodes.Stind_I1; break; case MetadataType.Int16: case MetadataType.UInt16: code = OpCodes.Stind_I2; break; case MetadataType.Int32: case MetadataType.UInt32: code = OpCodes.Stind_I4; break; case MetadataType.Int64: case MetadataType.UInt64: code = OpCodes.Stind_I8; break; case MetadataType.Single: code = OpCodes.Stind_R4; break; case MetadataType.Double: code = OpCodes.Stind_R8; break; case MetadataType.IntPtr: case MetadataType.UIntPtr: code = OpCodes.Stind_I; break; default: if (spec.ElementType.IsValueType) { il.Emit(Instruction.Create(OpCodes.Stobj, spec.ElementType)); } else { code = OpCodes.Stind_Ref; unboxing = false; } break; } if (unboxing) { il.Emit(Codes.Unbox(spec.ElementType)); } if (code != OpCodes.Nop) { il.Emit(code); } } }
/// <summary> /// Create an array variable containing the arguments of the method invocation. /// </summary> /// <param name="method">The method weaver.</param> /// <returns></returns> public Variable CreateArgumentArray(MethodEmitter method) { var parameters = method.Target.Parameters; var count = parameters.Count; var array = method.EmitLocal(Context.Finder.ObjectArray); var il = method.GetIL(); il.Emit(Codes.Nop); il.Emit(Codes.Int(count)); il.Emit(Codes.CreateArray(TypeSystem.ObjectReference)); il.Emit(Codes.Store(array)); for (int i = 0; i < count; i++) { var p = parameters[i]; var type = p.ParameterType; il.Emit(Codes.Load(array)); il.Emit(Codes.Int(i)); if (p.IsOut) { var spec = (TypeSpecification)type; if (spec.ElementType.IsValueType) { il.Emit(Codes.Init(spec.ElementType)); } else { il.Emit(Codes.Null); il.Emit(Codes.StoreArray); } continue; } il.Emit(Codes.Arg(p)); if (type.IsByReference) { var spec = (TypeSpecification)type; var boxing = true; switch (spec.ElementType.MetadataType) { case MetadataType.SByte: il.Emit(OpCodes.Ldind_I1); break; case MetadataType.Int16: il.Emit(OpCodes.Ldind_I2); break; case MetadataType.Int32: il.Emit(OpCodes.Ldind_I4); break; case MetadataType.Int64: case MetadataType.UInt64: il.Emit(OpCodes.Ldind_I8); break; case MetadataType.Boolean: case MetadataType.Byte: il.Emit(OpCodes.Ldind_U1); break; case MetadataType.UInt16: il.Emit(OpCodes.Ldind_U2); break; case MetadataType.UInt32: il.Emit(OpCodes.Ldind_U4); break; case MetadataType.Single: il.Emit(OpCodes.Ldind_R4); break; case MetadataType.Double: il.Emit(OpCodes.Ldind_R8); break; case MetadataType.IntPtr: case MetadataType.UIntPtr: il.Emit(OpCodes.Ldind_I); break; default: if (spec.ElementType.IsValueType) { il.Emit(Instruction.Create(OpCodes.Ldobj, spec.ElementType)); } else { il.Emit(OpCodes.Ldind_Ref); boxing = false; } break; } if (boxing) { il.Emit(Codes.Box(spec.ElementType)); } } else { il.Emit(Codes.Box(p.ParameterType)); } il.Emit(Codes.StoreArray); } return(array); }
/// <summary> /// Weaves the constructor interceptors for a provided type and interceptor information. /// </summary> /// <param name="weaver">The weaver.</param> /// <param name="item">The interceptors.</param> public void WeaveConstructorInterceptors(TypeEmitter weaver, ConstructorInterceptorInfo item) { var constructors = weaver.GetConstructors(); foreach (var init in item.Initializers) { var parameters = init.Parameters; if (parameters.Any(p => !p.IsOptional)) { throw new NotSupportedException($"Cannot use constructor injection against a method with non-optional parameters"); } var after = init.GetAttribute(Context.Finder.IInjectAfterInitializer); var before = init.GetAttribute(Context.Finder.IInjectBeforeInitializer); if (init.HasGenericParameters) { throw new NotSupportedException($"Cannot use constructor injection against a generic method"); } foreach (var ctor in constructors) { var il = ctor.GetIL(); var st = init.IsStatic; il.Body.SimplifyMacros(); il.Insert = CodeInsertion.Before; il.Position = before != null ? (il.GetConstructorBaseOrThis()?.Next ?? il.GetFirst()) : il.GetLast(); if (!st) { il.Emit(Codes.This); } var type = ctor.Parent.Target.GetGeneric(); var method = (MethodReference)init; if (type.IsGenericInstance) { var generic = (GenericInstanceType)type; method = init.MakeGeneric(generic.GenericArguments.ToArray()); } foreach (var param in parameters) { il.Emit(Codes.Load(param.Constant)); } il.Emit(st ? Codes.InvokeStatic(method) : Codes.Invoke(method)); if (method.IsReturn()) { il.Emit(Codes.Pop); } il.Body.InitLocals = true; il.Body.OptimizeMacros(); } if (after != null) { init.CustomAttributes.Remove(after); } if (before != null) { init.CustomAttributes.Remove(before); } } }
/// <summary> /// Weave an implementation of an event against the provided type. /// </summary> /// <param name="emitter">The emitter.</param> /// <param name="field">The attribute field.</param> /// <param name="evt">The event.</param> /// <param name="interfaceType">The type of the interface.</param> public void WeaveImplementedEvent(TypeEmitter emitter, Variable field, EventReference evt, TypeDefinition interfaceType) { var implemented = field.Type.GetEvent(evt.Name, evt.EventType); if (implemented == null) { implemented = field.Type.GetEvent($"{interfaceType.FullName}.{evt.Name}", evt.EventType); if (implemented == null) { throw new MissingMemberException($"Cannot implement '{field.Type.FullName}' as it does not implement event '{evt.Name}'"); } } var emitted = emitter.EmitEvent(evt.Name, implemented.EventType); var definition = evt.Resolve() ?? implemented.Resolve(); var hasAdd = emitted.HasAdd; var add = emitted.GetAdd(); if (!hasAdd) { add.Body.SimplifyMacros(); var ail = add.GetIL(); var interfaceAdd = definition.AddMethod.Import(); ail.Emit(Codes.Nop); ail.Emit(Codes.ThisIf(field)); ail.Emit(Codes.Load(field)); ail.Emit(Codes.Arg(add.IsStatic ? 0 : 1)); ail.Emit(Codes.Invoke(interfaceAdd.GetGeneric())); ail.Emit(Codes.Return); add.Body.OptimizeMacros(); } add.Body.InitLocals = true; var hasRemove = emitted.HasRemove; var remove = emitted.GetRemove(); if (!emitted.HasRemove) { remove.Body.SimplifyMacros(); var ril = remove.GetIL(); var interfaceRemove = definition.RemoveMethod.Import(); ril.Emit(Codes.Nop); ril.Emit(Codes.ThisIf(field)); ril.Emit(Codes.Load(field)); ril.Emit(Codes.Arg(remove.IsStatic ? 0 : 1)); ril.Emit(Codes.Invoke(interfaceRemove.GetGeneric())); ril.Emit(Codes.Return); remove.Body.OptimizeMacros(); } remove.Body.InitLocals = true; }
/// <summary> /// Weaves the interception logic of a property. /// </summary> /// <param name="parent"></param> /// <param name="item"></param> public void WeavePropertyInterceptors(TypeEmitter parent, PropertyInterceptorInfo item) { var property = item.Property; var getter = property.GetMethod != null ? new MethodEmitter(parent, property.GetMethod) : null; var setter = property.SetMethod != null ? new MethodEmitter(parent, property.SetMethod) : null; var count = item.Interceptors.Length; var weaver = new PropertyEmitter(parent, property); if (count == 0) { return; } var gInterceptors = new Variable[count]; var sInterceptors = new Variable[count]; for (int i = 0; i < count; i++) { var variables = CreateAttribute(weaver, getter, setter, item.Interceptors[i], item.Field); gInterceptors[i] = variables[0]; sInterceptors[i] = variables[1]; } var hasGetter = gInterceptors.Any(i => i != null); var hasSetter = sInterceptors.Any(i => i != null); var type = property.PropertyType; var info = CreatePropertyInfo(weaver); var field = property.GetBackingField(); var backing = (Variable)null; if (field != null) { var def = field.Resolve(); def.Attributes &= ~Mono.Cecil.FieldAttributes.InitOnly; backing = new Variable(def); } if (getter != null && hasGetter) { var il = getter.GetIL(); var result = getter.EmitLocal(property.PropertyType); il.Body.SimplifyMacros(); il.Position = il.GetFirst(); il.Insert = CodeInsertion.Before; if (backing != null) { il.Emit(Codes.ThisIf(backing)); il.Emit(Codes.Load(backing)); il.Emit(Codes.Store(result)); } else { if (type.IsValueType) { il.Emit(Codes.Address(result)); il.Emit(Codes.Init(type)); } else { il.Emit(Codes.Null); il.Emit(Codes.Store(result)); } } il.Try(); var leave = WeaveMethodReturnsRoute(getter, result, true); var cancel = il.EmitLabel(); var args = il.EmitLocal(Context.Finder.PropertyInterceptionArgs); il.Emit(getter.Target.IsStatic ? Codes.Null : Codes.This); il.Emit(Codes.Load(info)); il.Emit(Codes.Load(result)); il.Emit(Codes.Box(type)); il.Emit(Codes.Create(Context.Finder.PropertyInterceptionArgsCtor)); il.Emit(Codes.Store(args)); for (int i = 0; i < count; i++) { var inc = gInterceptors[i]; if (inc == null) { continue; } il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertyGetInterceptorOnGet)); } il.Position = leave; il.Finally(); for (int i = 0; i < count; i++) { var inc = gInterceptors[i]; if (inc == null) { continue; } il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertyGetInterceptorOnExit)); } il.Mark(cancel); il.EndTry(); il.Insert = CodeInsertion.After; if (setter != null || backing != null) { var unchanged = il.EmitLabel(); il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertyInterceptionArgsIsDirtyGet)); il.Emit(Codes.IfFalse(unchanged)); if (!getter.Target.IsStatic) { il.Emit(Codes.This); } if (backing == null) { var pcount = setter.Target.Parameters.Count - 1; for (int i = 0; i < pcount; i++) { il.Emit(Codes.Arg(setter.Target.Parameters[i])); } } il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertyInterceptionArgsValueGet)); il.Emit(Codes.Unbox(type)); if (backing != null) { il.Emit(Codes.Store(backing)); } else { il.Emit(Codes.Invoke(setter.Target)); } il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertyInterceptionArgsValueGet)); il.Emit(Codes.Unbox(type)); il.Emit(Codes.Store(result)); il.Emit(Codes.Nop); il.Mark(unchanged); } il.Emit(Codes.Load(result)); il.Body.InitLocals = true; il.Body.OptimizeMacros(); } if (setter != null && hasSetter) { var il = setter.GetIL(); il.Body.SimplifyMacros(); il.Position = il.GetFirst(); il.Insert = CodeInsertion.Before; var args = il.EmitLocal(Context.Finder.PropertyInterceptionArgs); var cancel = il.EmitLabel(); var argument = new Variable(setter.Target.Parameters.Last()); il.Try(); il.Emit(setter.Target.IsStatic ? Codes.Null : Codes.This); il.Emit(Codes.Load(info)); il.Emit(Codes.Load(argument)); il.Emit(Codes.Box(type)); il.Emit(Codes.Create(Context.Finder.PropertyInterceptionArgsCtor)); il.Emit(Codes.Store(args)); for (int i = 0; i < count; i++) { var inc = sInterceptors[i]; if (inc == null) { continue; } il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertySetInterceptorOnSet)); } il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertyInterceptionArgsIsDirtyGet)); il.Emit(Codes.IfFalse(cancel)); il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertyInterceptionArgsValueGet)); il.Emit(Codes.Unbox(argument.Type)); il.Emit(Codes.Store(argument)); il.Emit(Codes.Nop); il.Position = il.Position.Previous; il.Mark(cancel); il.Position = il.GetLast(); il.Emit(Codes.Leave(il.Position)); il.Finally(); for (int i = 0; i < count; i++) { var inc = sInterceptors[i]; if (inc == null) { continue; } il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); il.Emit(Codes.Load(args)); il.Emit(Codes.Invoke(Context.Finder.PropertySetInterceptorOnExit)); } il.EndTry(); il.Body.InitLocals = true; il.Body.OptimizeMacros(); } }
public Startup(IConfiguration configuration) { Configuration = configuration; Codes.Load(); }
/// <summary> /// Weaves the interception logic of a method. /// </summary> /// <param name="weaver">The method weaver.</param> /// <param name="item">The method information.</param> public void WeaveMethodInterceptors(MethodEmitter weaver, MethodInterceptorInfo item) { weaver.Body.SimplifyMacros(); var pAttributes = item.Parameters.SelectMany(p => p.Attributes.Select(a => new { p.Index, Attribute = a })).ToArray(); var mAttributes = item.MethodInterceptors; var rAttributes = item.ReturnInterceptors; var pInterceptors = new Variable[pAttributes.Length]; var mInterceptors = new Variable[item.MethodInterceptors.Length]; var rInterceptors = new Variable[rAttributes.Length]; var needsEnter = mAttributes.Any(m => m.HasRequiredMethod(Context.Finder.MethodInterceptorOnEnter)); var needsCatch = mAttributes.Any(m => m.HasRequiredMethod(Context.Finder.MethodInterceptorOnException)); var needsExit = mAttributes.Any(m => m.HasRequiredMethod(Context.Finder.MethodInterceptorOnExit)); var needsParams = pAttributes.Any(m => m.Attribute.HasRequiredMethod(Context.Finder.ParameterInterceptorOnEnter)); var needsReturns = rAttributes.Any(m => m.HasRequiredMethod(Context.Finder.MethodReturnInterceptorOnReturn)); var needsMethodArgs = mAttributes.Any(m => { return(m.AttributeType.GetMethod(Context.Finder.MethodInterceptorOnEnter).UsesParameter(0) || m.AttributeType.GetMethod(Context.Finder.MethodInterceptorOnException).UsesParameter(0) || m.AttributeType.GetMethod(Context.Finder.MethodInterceptorOnExit).UsesParameter(0)); }); var needsParamArgs = pAttributes.Any(m => m.Attribute.AttributeType.GetMethod(Context.Finder.ParameterInterceptorOnEnter).UsesParameter(0)); var needsReturnsArgs = rAttributes.Any(m => m.AttributeType.GetMethod(Context.Finder.MethodReturnInterceptorOnReturn).UsesParameter(0)); var needsMethodCopyArgs = mAttributes.Any(m => m.GetAttribute(Context.Finder.CompilationOptionsAttribute)?.GetProperty("CopyArguments", notFound: false) ?? false); if (!needsEnter && !needsCatch && !needsExit && !needsParams && !needsReturns) { return; } var method = weaver.Target; var il = weaver.GetIL(); il.Position = il.GetFirst(); il.Insert = CodeInsertion.Before; var result = (Variable)null; if (weaver.Target.IsReturn()) { var type = weaver.Target.ReturnType; result = weaver.EmitLocal(type); if (type.IsValueType) { il.Emit(Codes.Address(result)); il.Emit(Codes.Init(type)); } else { il.Emit(Codes.Null); il.Emit(Codes.Store(result)); } } var leave = WeaveMethodReturnsRoute(weaver, result, mInterceptors.Length > 0); var cancel = il.EmitLabel(); for (int i = 0, count = pInterceptors.Length; i < count; i++) { pInterceptors[i] = CreateAttribute(weaver, pAttributes[i].Attribute); } for (int i = 0, count = mInterceptors.Length; i < count; i++) { mInterceptors[i] = CreateAttribute(weaver, mAttributes[i]); } for (int i = 0, count = rInterceptors.Length; i < count; i++) { rInterceptors[i] = CreateAttribute(weaver, rAttributes[i]); } foreach (var attribute in mAttributes) { weaver.Target.CustomAttributes.Remove(attribute); } foreach (var attribute in pAttributes) { if (attribute.Index == -1) { weaver.Target.CustomAttributes.Remove(attribute.Attribute); } else { weaver.Target.Parameters[attribute.Index].CustomAttributes.Remove(attribute.Attribute); } } foreach (var attribute in rAttributes) { weaver.Target.MethodReturnType.CustomAttributes.Remove(attribute); } foreach (var parameter in item.Parameters) { foreach (var attribute in parameter.Attributes) { if (parameter.Index == -1) { weaver.Target.CustomAttributes.Remove(attribute); } else { weaver.Target.Parameters[parameter.Index].CustomAttributes.Remove(attribute); } } } var hasMethod = mInterceptors.Length > 0; var hasParams = pInterceptors.Length > 0; var hasReturn = rInterceptors.Length > 0; var arguments = hasMethod ? CreateArgumentArray(weaver) : null; var invocation = CreateMethodInfo(weaver); var mEventArgs = hasMethod && needsMethodArgs?weaver.EmitLocal(Context.Finder.MethodInterceptionArgs, name : "methodEventArgs") : null; if (hasMethod || hasReturn) { if (hasMethod && needsMethodArgs) { il.Emit(method.IsStatic ? Codes.Null : Codes.This); il.Emit(Codes.Load(arguments)); if (result == null) { il.Emit(Codes.Null); } else { il.Emit(Codes.Load(result)); il.Emit(Codes.Box(result.Type)); } il.Emit(Codes.Load(invocation)); il.Emit(Codes.Create(Context.Finder.MethodInterceptionArgsCtor)); il.Emit(Codes.Store(mEventArgs)); } il.Try(); } if (hasParams && needsParams) { var pEventArgs = needsParamArgs ? weaver.EmitLocal(Context.Finder.ParameterInterceptionArgs) : null; for (int i = 0, count = pInterceptors.Length; i < count; i++) { var inc = pInterceptors[i]; var parameters = pAttributes[i].Index == -1 ? method.Parameters.ToArray() : new[] { method.Parameters[pAttributes[i].Index] }; for (int j = 0, pCount = parameters.Length; j < pCount; j++) { var prm = parameters[j]; var pVariable = new Variable(prm); var pInfo = CreateParameterInfo(weaver, prm); var needsCopyArgs = pAttributes[i].Attribute.GetAttribute(Context.Finder.CompilationOptionsAttribute)?.GetProperty("CopyArguments", notFound: false) ?? false; if (needsParamArgs) { il.Emit(method.IsStatic ? Codes.Null : Codes.This); il.Emit(Codes.Load(pInfo)); il.Emit(Codes.Arg(pVariable)); il.Emit(Codes.Box(prm.ParameterType)); il.Emit(Codes.Create(Context.Finder.ParameterInterceptionArgsCtor)); il.Emit(Codes.Store(pEventArgs)); } il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); if (needsParamArgs) { il.Emit(Codes.Load(pEventArgs)); } else { il.Emit(Codes.Null); } il.Emit(Codes.Invoke(Context.Finder.ParameterInterceptorOnEnter)); if (needsParamArgs && !prm.IsOut && needsCopyArgs) { il.Emit(Codes.Load(pEventArgs)); il.Emit(Codes.Invoke(Context.Finder.ParameterInterceptionArgsValueGet)); il.Emit(Codes.Unbox(prm.ParameterType)); il.Emit(Codes.Store(pVariable)); } } } } if (hasMethod || hasReturn) { if (hasMethod && needsEnter) { for (int i = 0, count = mInterceptors.Length; i < count; i++) { var inc = mInterceptors[i]; var att = mAttributes[i]; if (att.HasRequiredMethod("OnEnter")) { il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); if (needsMethodArgs) { il.Emit(Codes.Load(mEventArgs)); } else { il.Emit(Codes.Null); } il.Emit(Codes.Invoke(Context.Finder.MethodInterceptorOnEnter)); if (needsMethodArgs) { var proceed = il.EmitLabel(); il.Emit(Codes.Load(mEventArgs)); il.Emit(Codes.Invoke(Context.Finder.MethodInterceptionArgsCancelGet)); il.Emit(Codes.IfFalse(proceed)); if (result != null && needsMethodArgs) { il.Emit(Codes.Load(mEventArgs)); il.Emit(Codes.Invoke(Context.Finder.MethodInterceptionArgsReturnGet)); il.Emit(Codes.Unbox(result.Type)); il.Emit(Codes.Store(result)); } il.Emit(Codes.Leave(leave)); il.Mark(proceed); } } } if (needsMethodArgs && needsMethodCopyArgs && arguments != null) { CopyArgumentArrayToParameters(weaver, arguments); } } il.Position = leave; if (hasMethod && needsCatch) { var exception = il.EmitLocal(Context.Finder.Exception); il.Catch(exception); for (int i = 0, count = mInterceptors.Length; i < count; i++) { var inc = mInterceptors[i]; var att = mAttributes[i]; if (att.HasRequiredMethod("OnException")) { il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); if (needsMethodArgs) { il.Emit(Codes.Load(mEventArgs)); } else { il.Emit(Codes.Null); } il.Emit(Codes.Load(exception)); il.Emit(Codes.Invoke(Context.Finder.MethodInterceptorOnException)); } } if (needsExit || needsReturns) { il.Emit(Codes.Nop); il.Emit(Codes.Leave(leave)); } } if (needsExit || needsReturns) { il.Finally(leave); } if (hasMethod) { if (result != null && needsMethodArgs) { il.Emit(Codes.Load(mEventArgs)); il.Emit(Codes.Load(result)); il.Emit(Codes.Box(weaver.Target.ReturnType)); il.Emit(Codes.Invoke(Context.Finder.MethodInterceptionArgsReturnSet)); } if (needsExit) { for (int i = 0, count = mInterceptors.Length; i < count; i++) { var inc = mInterceptors[i]; var att = mAttributes[i]; if (att.HasRequiredMethod("OnExit")) { il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); if (needsMethodArgs) { il.Emit(Codes.Load(mEventArgs)); } else { il.Emit(Codes.Null); } il.Emit(Codes.Invoke(Context.Finder.MethodInterceptorOnExit)); } } } il.Mark(cancel); } if (hasReturn && needsReturns && result != null) { var rEventArgs = needsReturnsArgs ? il.EmitLocal(Context.Finder.MethodReturnInterceptionArgs) : null; if (needsReturnsArgs) { il.Emit(method.IsStatic ? Codes.Null : Codes.This); il.Emit(Codes.Load(result)); il.Emit(Codes.Box(result.Type)); il.Emit(Codes.Load(invocation)); il.Emit(Codes.Create(Context.Finder.MethodReturnInterceptionArgsCtor)); il.Emit(Codes.Store(rEventArgs)); } for (int i = 0, count = rInterceptors.Length; i < count; i++) { var inc = rInterceptors[i]; var att = rAttributes[i]; if (att.HasRequiredMethod("OnReturn")) { il.Emit(Codes.ThisIf(inc)); il.Emit(Codes.Load(inc)); if (needsReturnsArgs) { il.Emit(Codes.Load(rEventArgs)); } else { il.Emit(Codes.Null); } il.Emit(Codes.Invoke(Context.Finder.MethodReturnInterceptorOnReturn)); } } if (needsReturnsArgs) { il.Emit(Codes.Load(rEventArgs)); il.Emit(Codes.Invoke(Context.Finder.MethodReturnInterceptionArgsValueGet)); il.Emit(Codes.Unbox(result.Type)); il.Emit(Codes.Store(result)); } } if (!needsExit && !needsReturns) { il.Emit(Codes.Nop); il.Emit(Codes.Leave(leave)); } il.EndTry(); il.Insert = CodeInsertion.After; if (hasMethod && arguments != null) { CopyArgumentArrayToReferences(weaver, arguments); } if (hasMethod && needsMethodArgs && result != null) { il.Emit(Codes.Nop); il.Emit(Codes.Load(mEventArgs)); il.Emit(Codes.Invoke(Context.Finder.MethodInterceptionArgsReturnGet)); il.Emit(Codes.Unbox(weaver.Target.ReturnType)); } else if (hasMethod && result != null) { il.Emit(Codes.Nop); il.Emit(Codes.Load(result)); } } weaver.Body.InitLocals = true; weaver.Body.OptimizeMacros(); }