Exemplo n.º 1
0
        /// <summary>
        /// Replaces the <paramref name="oldInstruction"/> with a set of new instructions.
        /// </summary>
        /// <param name="oldInstruction">The instruction currently being evaluated.</param>
        /// <param name="hostMethod">The method that contains the target instruction.</param>
        /// <param name="IL">The CilWorker that will be used to emit the method body instructions.</param>
        protected override void Replace(Instruction oldInstruction, MethodDefinition hostMethod, CilWorker IL)
        {
            var targetField = (FieldReference) oldInstruction.Operand;
            var fieldType = targetField.FieldType;
            var isSetter = oldInstruction.OpCode == OpCodes.Stsfld || oldInstruction.OpCode == OpCodes.Stfld;

            if (isSetter)
            {
                hostMethod.Body.InitLocals = true;
                // Save the setter argument and box it if necessary
                if (fieldType.IsValueType || fieldType is GenericParameter)
                    IL.Emit(OpCodes.Box, fieldType);

                IL.Emit(OpCodes.Stloc, _currentArgument);
            }

            // There's no need to push the current object instance
            // since the this pointer is pushed prior to the field call
            if (hostMethod.IsStatic)
                IL.Emit((OpCodes.Ldnull));

            // Push the current method
            var module = hostMethod.DeclaringType.Module;

            // Push the current method onto the stack
            IL.PushMethod(hostMethod, module);

            // Push the current field onto the stack
            IL.PushField(targetField, module);

            // Push the host type onto the stack
            IL.PushType(hostMethod.DeclaringType, module);

            // Create the IFieldInterceptionContext instance
            IL.Emit(OpCodes.Newobj, _fieldContextCtor);
            IL.Emit(OpCodes.Stloc, _fieldContext);

            var skipInterception = IL.Create(OpCodes.Nop);
            // Obtain an interceptor instance
            if (hostMethod.IsStatic)
            {
                IL.Emit(OpCodes.Ldloc, _fieldContext);
                IL.Emit(OpCodes.Call, _getInterceptor);
            }
            else
            {
                IL.Emit(OpCodes.Ldarg_0);
                IL.Emit(OpCodes.Isinst, _fieldInterceptionHostType);
                IL.Emit(OpCodes.Brfalse, skipInterception);

                IL.Emit(OpCodes.Ldarg_0);
                IL.Emit(OpCodes.Isinst, _fieldInterceptionHostType);
                IL.Emit(OpCodes.Callvirt, _getInstanceInterceptor);
            }

            // The field interceptor cannot be null
            IL.Emit(OpCodes.Stloc, _fieldInterceptor);
            IL.Emit(OpCodes.Ldloc, _fieldInterceptor);
            IL.Emit(OpCodes.Brfalse, skipInterception);

            // if (FieldInterceptor.CanIntercept(context) {
            IL.Emit(OpCodes.Ldloc, _fieldInterceptor);
            IL.Emit(OpCodes.Ldloc, _fieldContext);
            IL.Emit(OpCodes.Callvirt, _canIntercept);
            IL.Emit(OpCodes.Brfalse, skipInterception);

            var isGetter = oldInstruction.OpCode == OpCodes.Ldsfld || oldInstruction.OpCode == OpCodes.Ldfld;

            var endLabel = IL.Create(OpCodes.Nop);

            //Call the interceptor instead of the getter or setter
            if (isGetter)
            {
                IL.Emit(OpCodes.Ldloc, _fieldInterceptor);
                IL.Emit(OpCodes.Ldloc, _fieldContext);
                IL.Emit(OpCodes.Callvirt, _getValue);
                IL.Emit(OpCodes.Unbox_Any, fieldType);
            }

            if (isSetter)
            {
                // Push the 'this' pointer for instance field setters
                if (!hostMethod.IsStatic)
                    IL.Emit(OpCodes.Ldarg_0);

                IL.Emit(OpCodes.Ldloc, _fieldInterceptor);
                IL.Emit(OpCodes.Ldloc, _fieldContext);
                IL.Emit(OpCodes.Ldloc, _currentArgument);

                // Unbox the setter value
                IL.Emit(OpCodes.Unbox_Any, fieldType);

                IL.Emit(OpCodes.Callvirt, _setValue);

                // Set the actual field value
                IL.Emit(OpCodes.Unbox_Any, fieldType);
                IL.Emit(oldInstruction.OpCode, targetField);
            }

            IL.Emit(OpCodes.Br, endLabel);

            // }
            IL.Append(skipInterception);

            // else {

            // Load the original field
            if (!hostMethod.IsStatic)
                IL.Emit(OpCodes.Ldarg_0);

            if (isSetter)
            {
                IL.Emit(OpCodes.Ldloc, _currentArgument);

                // Unbox the setter value
                IL.Emit(OpCodes.Unbox_Any, fieldType);
            }

            IL.Emit(oldInstruction.OpCode, targetField);

            // }

            IL.Append(endLabel);
        }