Пример #1
0
    /// <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);
    }
Пример #2
0
    /// <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);
    }
Пример #3
0
    /// <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));
        }
    }
Пример #4
0
    /// <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);
    }
Пример #5
0
    /// <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;
        }
    }
Пример #6
0
    /// <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));
        }
    }
Пример #7
0
    /// <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);
    }
Пример #8
0
    /// <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()));
    }
Пример #9
0
    /// <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);
    }
Пример #10
0
    /// <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;
    }
Пример #11
0
    /// <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);
            }
        }
    }
Пример #12
0
    /// <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);
    }
Пример #13
0
    /// <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);
            }
        }
    }
Пример #14
0
    /// <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;
    }
Пример #15
0
    /// <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();
        }
    }
Пример #16
0
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;

            Codes.Load();
        }
Пример #17
0
    /// <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();
    }