Ejemplo n.º 1
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);
    }
Ejemplo n.º 2
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();
        }
    }
Ejemplo n.º 3
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();
    }