/// <summary>
        /// Weaves the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="block">The block.</param>
        public void Weave(WeavingContext context, InstructionBlock block)
        {
            LocalVariableSymbol parameters = block.DefineLocalVariable(context.Method.Module.FindType(typeof(ParameterDictionary), BindingOptions.Default), NameGenerator.Generate("parameters"));

            InstructionSequence entrySequence = context.Method.MethodBody.CreateInstructionSequence();
            block.AddInstructionSequence(entrySequence, NodePosition.Before, null);

            InstructionWriter writer = context.InstructionWriter;
            writer.AttachInstructionSequence(entrySequence);
            writer.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);

            writer.EmitInstructionMethod(OpCodeNumber.Call, context.Method.Module.FindMethod(typeof(InternalHelperMethods).GetMethod("CreateParameterCollection"), BindingOptions.Default));

            IMethod add = context.Method.Module.FindMethod(typeof(ParameterDictionary).GetMethod("Add", new[] { typeof(string), typeof(object) }), BindingOptions.Default);

            short parameterIndex = context.Method.IsStatic ? (short)0 : (short)1;
            foreach (var parameter in method.Parameters)
            {
                writer.EmitInstruction(OpCodeNumber.Dup);

                writer.EmitInstructionString(OpCodeNumber.Ldstr, new LiteralString(parameter.Name));
                writer.EmitInstructionInt16(OpCodeNumber.Ldarg, parameterIndex++);

                if (parameter.ParameterType.GetSystemType(null, null).IsValueType)
                {
                    writer.EmitInstructionType(OpCodeNumber.Box, parameter.ParameterType);
                }

                writer.EmitInstructionMethod(OpCodeNumber.Callvirt, add);
            }

            writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, parameters);

            writer.DetachInstructionSequence();
        }
        public static void AddPropertyGuard(PropertyDeclaration property, MethodBodyTransformationContext context, InstructionBlock block, InstructionWriter writer)
        {
            var propertyType = property.PropertyType;
            var methodBody = block.MethodBody;

            var sequence = block.AddInstructionSequence(null, NodePosition.After, null);
            if (sequence == null) return;

            var oldValueVariable =
                block.DefineLocalVariable(propertyType, string.Format("old{0}Value", property.Name));
            var assets = GetTransformationAssets(property.Module);

            writer.AttachInstructionSequence(sequence);
            var isLocationBinding = CheckIfIsLocationBinding(methodBody,assets);
            if (isLocationBinding) {
                writer.AssignValue_LocalVariable(oldValueVariable
                    , () => writer.Call_MethodOnTarget(property.GetGetter()
                        ,
                        () => {
                            //Load the instance parameter of the SetValue method
                            //and convert it to the type
                            writer.EmitInstruction(OpCodeNumber.Ldarg_1);
                            //writer.EmitInstructionLoadIndirect(Assets.ObjectTypeSignature);
                            writer.EmitInstructionType(OpCodeNumber.Ldobj, assets.ObjectTypeSignature);
                            writer.EmitConvertFromObject(property.Parent);
                        }
                    )
                );
                //On the location binding the value parameter is at psotion 3
                writer.EmitInstruction(OpCodeNumber.Ldarg_3);
            } else {
                writer.AssignValue_LocalVariable(oldValueVariable,
                                                    () => writer.Get_PropertyValue(property));
                //For a normal property the value parameter is at position 1
                writer.EmitInstruction(OpCodeNumber.Ldarg_1);
            }
            if (propertyType.IsStruct()) {
                writer.EmitInstructionType(OpCodeNumber.Box, propertyType);
            }
            writer.Box_LocalVariableIfNeeded(oldValueVariable);
            var isPrimitive = propertyType.IsPrimitive();
            if (isPrimitive) {
                writer.Compare_Primitives();
            } else {
                //TODO: Try and use the equality operator when present
                writer.Compare_Objects(assets.ObjectEqualsMethod);
            }
            //writer.Leave_IfTrue(_context.LeaveBranchTarget);
            writer.Leave_IfTrue(context.LeaveBranchTarget);
            writer.DetachInstructionSequence();
        }
        /// <summary>
        /// Creates a new method body and assigns it to <paramref name="hostMethod"/>. The method body looks as described in <see cref="CreatedEmptyMethod"/>.
        /// </summary>
        /// <param name="instructionWriter">A <b>detached</b> instruction writer.</param>
        /// <param name="hostMethod">The method without body. The body will be assigned to this method.</param>
        /// <returns>References to points in the method body.</returns>
        public static CreatedEmptyMethod CreateModifiableMethodBody(InstructionWriter instructionWriter, MethodDefDeclaration hostMethod)
        {
            // Create a new method body to host the pipeline.
            hostMethod.MethodBody = new MethodBodyDeclaration();
            InstructionBlock rootInstructionBlock = hostMethod.MethodBody.RootInstructionBlock = hostMethod.MethodBody.CreateInstructionBlock();

            InstructionBlock    sequencePointBlock    = rootInstructionBlock.AddChildBlock();
            InstructionSequence sequencePointSequence = sequencePointBlock.AddInstructionSequence();

            instructionWriter.AttachInstructionSequence(sequencePointSequence);
            instructionWriter.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);
            instructionWriter.EmitInstruction(OpCodeNumber.Nop);
            instructionWriter.DetachInstructionSequence();

            InstructionBlock    implementationBlock = rootInstructionBlock.AddChildBlock();
            InstructionBlock    returnBlock         = rootInstructionBlock.AddChildBlock();
            InstructionSequence returnSequence      = returnBlock.AddInstructionSequence();

            instructionWriter.AttachInstructionSequence(returnSequence);
            instructionWriter.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);
            LocalVariableSymbol returnVariable;

            if (!hostMethod.ReturnParameter.ParameterType.IsIntrinsic(IntrinsicType.Void))
            {
                hostMethod.MethodBody.InitLocalVariables = true;
                returnVariable = rootInstructionBlock.DefineLocalVariable(
                    hostMethod.ReturnParameter.ParameterType,
                    DebuggerSpecialNames.GetVariableSpecialName(hostMethod.Domain, "returnValue", DebuggerSpecialVariableKind.ReturnValue)
                    );
                instructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, returnVariable);
            }
            else
            {
                returnVariable = null;
            }

            instructionWriter.EmitInstruction(OpCodeNumber.Ret);
            instructionWriter.DetachInstructionSequence();
            return(new CreatedEmptyMethod(hostMethod, implementationBlock, returnVariable, returnSequence));
        }
        private void WeaveException(WeavingContext context, InstructionBlock block)
        {
            LogLevel level = this.attribute.ExceptionLevel;
              string text = this.attribute.ExceptionText;

              // Inject the logging code only if the logging is turned on.
              if (this.attribute.ExceptionLevel != LogLevel.None)
              {
            // Method being woven and the type the method is declared in.
            MethodDefDeclaration wovenMethod = context.Method;
            TypeDefDeclaration wovenType = wovenMethod.DeclaringType;

            // Objects that contain required methods, fields, etc.
            LogLevelSupportItem supportItem = this.parent.GetSupportItem(level);
            PerTypeLoggingData perTypeLoggingData = this.parent.GetPerTypeLoggingData(wovenType);

            // Get the tokens for the message template.
            StringBuilder messageFormatString = new StringBuilder();
            List<IMessageToken> nonStaticTokens = new List<IMessageToken>();
            List<IMessageToken> messageParts = TemplateParser.Tokenize(text, wovenMethod, attribute.IncludeParamName);
            MakeFormatString(messageParts, messageFormatString, nonStaticTokens);

            // As log4net does not provide an overload for the LogXXX() methods which would accept
            // both exception and array of arguments for a format string, disallow usage of dynamic
            // tokens in the template.
            if (nonStaticTokens.Count > 0)
            {
              throw new FormatException("Message for logging exception can contain only placeholders whose value can be expanded at weaving time.");
            }

            // Variable that stores the reference to the thrown exception.
            LocalVariableSymbol exception = block.DefineLocalVariable(context.Method.Module.FindType(typeof(Exception), BindingOptions.Default), "~ex~{0}");

            // Sequence that contains code that checks if the logging is enabled and logs the message.
            InstructionSequence logExceptionSequence = context.Method.MethodBody.CreateInstructionSequence();
            block.AddInstructionSequence(logExceptionSequence, NodePosition.Before, null);
            // Sequence that contains code that is executed after logging.
            InstructionSequence afterLoggingSequence = context.Method.MethodBody.CreateInstructionSequence();
            block.AddInstructionSequence(afterLoggingSequence, NodePosition.After, logExceptionSequence);

            // Emit code that checks if the logging is enabled and logs the message.
            context.InstructionWriter.AttachInstructionSequence(logExceptionSequence);
            context.InstructionWriter.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);
            context.InstructionWriter.EmitInstructionLocalVariable(OpCodeNumber.Stloc_S, exception);
            EmitLoggingEnabledCheck(context.InstructionWriter, supportItem, perTypeLoggingData, afterLoggingSequence);
            EmitLogStringException(context.InstructionWriter, perTypeLoggingData.Log, supportItem.LogStringExceptionMethod, messageFormatString.ToString(), exception);
            context.InstructionWriter.DetachInstructionSequence();

            // After logging is finished (or skipped), rethrow the exception.
            context.InstructionWriter.AttachInstructionSequence(afterLoggingSequence);
            context.InstructionWriter.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);
            context.InstructionWriter.EmitInstruction(OpCodeNumber.Rethrow);
            context.InstructionWriter.DetachInstructionSequence();
              }
              else
              {
            // Logging is turned off, just rethrow the exception.
            InstructionSequence rethrowSequence = context.Method.MethodBody.CreateInstructionSequence();
            block.AddInstructionSequence(rethrowSequence, NodePosition.Before, null);
            context.InstructionWriter.AttachInstructionSequence(rethrowSequence);
            context.InstructionWriter.EmitSymbolSequencePoint(SymbolSequencePoint.Hidden);
            context.InstructionWriter.EmitInstruction(OpCodeNumber.Rethrow);
            context.InstructionWriter.DetachInstructionSequence();
              }
        }
        /// <summary>
        /// Emits the MSIL that creates local variable and initializes it with array of objects representing the specified tokens.
        /// </summary>
        /// <param name="context">Context for the weaving.</param>
        /// <param name="block">Block where the code has to be injected.</param>
        /// <param name="nonStaticTokens">List of tokens that the object array is created for.</param>
        /// <returns>Local variable that stores the reference to the array.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="context"/>, <paramref name="block"/> or <paramref name="nonStaticTokens"/> is <see langword="null"/>.</exception>
        /// <remarks>
        /// <para>Code emitted by this method makes no assumptions on the state of the evaluation stack 
        /// and it leaves the stack unmodified.</para>
        /// </remarks>
        private LocalVariableSymbol EmitCreateFormatArgumentArray(WeavingContext context, InstructionBlock block, IList<IMessageToken> nonStaticTokens)
        {
            if (context == null)
              {
            throw new ArgumentNullException("context");
              }
              if (block == null)
              {
            throw new ArgumentNullException("block");
              }
              if (nonStaticTokens == null)
              {
            throw new ArgumentNullException("nonStaticTokens");
              }

              InstructionWriter emitter = context.InstructionWriter;

              // Array that store arguments for the formatting.
              LocalVariableSymbol args = block.DefineLocalVariable(context.Method.Module.FindType(typeof(object[]), BindingOptions.Default), "~args~{0}");

              // Create the array for storing agruments for formatting (contains only dyncamic tokens).
              emitter.EmitInstructionInt32(OpCodeNumber.Ldc_I4, nonStaticTokens.Count);
              emitter.EmitInstructionType(OpCodeNumber.Newarr, this.parent.ObjectType);
              // Save the array into the local variable because it will be used multiple times.
              emitter.EmitInstructionLocalVariable(OpCodeNumber.Stloc, args);

              // Fill the array with the data.
              for (int index = 0; index < nonStaticTokens.Count; index++)
              {
            // Array to store the data in.
            emitter.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, args);
            // Position in the array to store the data at.
            emitter.EmitInstructionInt32(OpCodeNumber.Ldc_I4, index);
            // Let the token generate the IL that pushes the argument onto the stack.
            nonStaticTokens[index].Emit(context);
            // Finally: store the generated object into the array at the given position.
            emitter.EmitInstruction(OpCodeNumber.Stelem_Ref);
              }

              return args;
        }
            public override void Implement(TransformationContext context)
            {
                TypeDefDeclaration typeDef = (TypeDefDeclaration)context.TargetElement;

                ModuleDeclaration module         = this.AspectWeaver.Module;
                ITypeSignature    baseEntityType = module.Cache.GetType(typeof(BaseEntity));

                // Find the base method.
                IMethod         baseCopyToMethod = null;
                IType           baseTypeCursor   = typeDef;
                MethodSignature methodSignature  =
                    new MethodSignature(module, CallingConvention.HasThis, module.Cache.GetIntrinsic(IntrinsicType.Void),
                                        new[] { baseEntityType }, 0);

                while (baseCopyToMethod == null && baseTypeCursor != null)
                {
                    TypeDefDeclaration baseTypeCursorTypeDef = baseTypeCursor.GetTypeDefinition();

                    baseCopyToMethod =
                        baseTypeCursorTypeDef.Methods.GetMethod("CopyTo",
                                                                methodSignature.Translate(baseTypeCursorTypeDef.Module),
                                                                BindingOptions.OnlyExisting |
                                                                BindingOptions.DontThrowException);

                    baseTypeCursor = baseTypeCursorTypeDef.BaseType;
                }

                if (baseCopyToMethod == null)
                {
                    throw new AssertionFailedException("Could not find a method CopyTo.");
                }

                if (baseCopyToMethod.DeclaringType == typeDef)
                {
                    return;
                }


                // Declare the method.
                MethodDefDeclaration methodDef = new MethodDefDeclaration
                {
                    Name       = "CopyTo",
                    Attributes =
                        (MethodAttributes.Family | MethodAttributes.ReuseSlot |
                         MethodAttributes.Virtual),
                    CallingConvention = CallingConvention.HasThis
                };

                typeDef.Methods.Add(methodDef);
                methodDef.CustomAttributes.Add(this.AspectWeaver.AspectInfrastructureTask.WeavingHelper.GetDebuggerNonUserCodeAttribute());

                // Define parameter.
                methodDef.ReturnParameter = new ParameterDeclaration
                {
                    ParameterType = module.Cache.GetIntrinsic(IntrinsicType.Void),
                    Attributes    = ParameterAttributes.Retval
                };

                ParameterDeclaration cloneParameter =
                    new ParameterDeclaration(0, "clone", baseEntityType);

                methodDef.Parameters.Add(cloneParameter);

                // Define the body
                MethodBodyDeclaration methodBody = new MethodBodyDeclaration();

                methodDef.MethodBody = methodBody;
                InstructionBlock instructionBlock = methodBody.CreateInstructionBlock();

                methodBody.RootInstructionBlock = instructionBlock;
                InstructionSequence sequence = methodBody.CreateInstructionSequence();

                instructionBlock.AddInstructionSequence(sequence, NodePosition.After, null);
                using (InstructionWriter writer = new InstructionWriter())
                {
                    writer.AttachInstructionSequence(sequence);

                    // Cast the argument and store it in a local variable.
                    IType typeSpec = GenericHelper.GetTypeCanonicalGenericInstance(typeDef);
                    LocalVariableSymbol castedCloneLocal = instructionBlock.DefineLocalVariable(typeSpec, "typedClone");
                    writer.EmitInstruction(OpCodeNumber.Ldarg_1);
                    writer.EmitInstructionType(OpCodeNumber.Castclass, typeSpec);
                    writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, castedCloneLocal);


                    // TODO: support generic base types.


                    // Call the base method.
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstruction(OpCodeNumber.Ldarg_1);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, (IMethod)baseCopyToMethod.Translate(typeDef.Module));

                    // Loop on all fields and clone cloneable ones.
                    TypeRefDeclaration cloneableTypeRef = (TypeRefDeclaration)
                                                          module.Cache.GetType(typeof(ICloneable));
                    MethodRefDeclaration cloneMethodRef = (MethodRefDeclaration)cloneableTypeRef.MethodRefs.GetMethod(
                        "Clone",
                        new MethodSignature(
                            module,
                            CallingConvention.HasThis,
                            module.Cache.GetIntrinsic(
                                IntrinsicType.Object),
                            new ITypeSignature[0], 0),
                        BindingOptions.Default);

                    foreach (FieldDefDeclaration fieldDef in typeDef.Fields)
                    {
                        if ((fieldDef.Attributes & FieldAttributes.Static) != 0)
                        {
                            continue;
                        }

                        if (fieldDef.FieldType == module.Cache.GetIntrinsic(IntrinsicType.String))
                        {
                            continue;
                        }

                        // Does not work?
                        //bool cloneable = fieldDef.FieldType.Inherits(cloneableTypeRef, GenericMap.Empty);
                        bool cloneable = typeof(ICloneable).IsAssignableFrom(fieldDef.FieldType.GetSystemType(null, null));


                        if (cloneable)
                        {
                            IField fieldSpec   = GenericHelper.GetFieldCanonicalGenericInstance(fieldDef);
                            bool   isValueType =
                                fieldSpec.FieldType.BelongsToClassification(TypeClassifications.ValueType).Equals(
                                    NullableBool.True);

                            InstructionSequence nextSequence = null;
                            if (!isValueType)
                            {
                                nextSequence = methodBody.CreateInstructionSequence();
                                writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                                writer.EmitInstructionField(OpCodeNumber.Ldfld, fieldSpec);
                                writer.EmitBranchingInstruction(OpCodeNumber.Brfalse, nextSequence);
                            }
                            writer.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, castedCloneLocal);
                            writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                            writer.EmitInstructionField(OpCodeNumber.Ldfld, fieldSpec);
                            if (isValueType)
                            {
                                writer.EmitInstructionType(OpCodeNumber.Box, fieldSpec.FieldType);
                            }
                            //writer.EmitInstructionType(OpCodeNumber.Castclass, cloneableTypeRef);
                            writer.EmitInstructionMethod(OpCodeNumber.Callvirt, cloneMethodRef);
                            if (isValueType)
                            {
                                writer.EmitInstructionType(OpCodeNumber.Unbox, fieldSpec.FieldType);
                                writer.EmitInstructionType(OpCodeNumber.Ldobj, fieldSpec.FieldType);
                            }
                            else
                            {
                                writer.EmitInstructionType(OpCodeNumber.Castclass, fieldSpec.FieldType);
                            }
                            writer.EmitInstructionField(OpCodeNumber.Stfld, fieldSpec);


                            if (!isValueType)
                            {
                                writer.DetachInstructionSequence();
                                instructionBlock.AddInstructionSequence(nextSequence, NodePosition.After, sequence);
                                sequence = nextSequence;
                                writer.AttachInstructionSequence(sequence);
                            }
                        }
                    }

                    writer.EmitInstruction(OpCodeNumber.Ret);
                    writer.DetachInstructionSequence();
                }
            }
Ejemplo n.º 7
0
            public override void Implement(TransformationContext context)
            {
                ModuleDeclaration module = this.AspectWeaver.Module;

                TypeDefDeclaration typeDef = (TypeDefDeclaration)context.TargetElement;

                // Declare the method.
                MethodDefDeclaration methodDef = new MethodDefDeclaration
                {
                    Name              = "AutoGeneratedValidate",
                    Attributes        = (MethodAttributes.Family | MethodAttributes.ReuseSlot | MethodAttributes.Virtual),
                    CallingConvention = CallingConvention.HasThis
                };

                typeDef.Methods.Add(methodDef);
                methodDef.CustomAttributes.Add(this.AspectWeaver.AspectInfrastructureTask.WeavingHelper.GetDebuggerNonUserCodeAttribute());

                // Define parameter.
                methodDef.ReturnParameter = new ParameterDeclaration
                {
                    ParameterType = module.Cache.GetIntrinsic(IntrinsicType.Void),
                    Attributes    = ParameterAttributes.Retval
                };

                // Define the body
                MethodBodyDeclaration methodBody = new MethodBodyDeclaration();

                methodDef.MethodBody = methodBody;
                InstructionBlock instructionBlock = methodBody.CreateInstructionBlock();

                methodBody.RootInstructionBlock = instructionBlock;
                InstructionSequence sequence = methodBody.CreateInstructionSequence();

                instructionBlock.AddInstructionSequence(sequence, NodePosition.After, null);

                using (InstructionWriter writer = new InstructionWriter())
                {
                    writer.AttachInstructionSequence(sequence);

                    // Find the base method.
                    IMethod         baseValidateMethod = null;
                    IType           baseTypeCursor     = typeDef.BaseType;
                    MethodSignature methodSignature    =
                        new MethodSignature(module, CallingConvention.HasThis, module.Cache.GetIntrinsic(IntrinsicType.Void),
                                            new ITypeSignature[0], 0);

                    while (baseValidateMethod == null)
                    {
                        TypeDefDeclaration baseTypeCursorTypeDef = baseTypeCursor.GetTypeDefinition();

                        baseValidateMethod =
                            baseTypeCursorTypeDef.Methods.GetMethod("AutoGeneratedValidate",
                                                                    methodSignature.Translate(baseTypeCursorTypeDef.Module),
                                                                    BindingOptions.OnlyExisting |
                                                                    BindingOptions.DontThrowException);

                        baseTypeCursor = baseTypeCursorTypeDef.BaseType;
                    }

                    // TODO: support generic base types.

                    // Call the base method.
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, (IMethod)baseValidateMethod.Translate(typeDef.Module));

                    // Make an array with the boxed field values.
                    TypeValidationAspect aspect = (TypeValidationAspect)this.AspectWeaverInstance.Aspect;
                    LocalVariableSymbol  fieldValuesArrayLocal = instructionBlock.DefineLocalVariable(
                        module.Cache.GetType(typeof(object[])), "fieldValues");
                    writer.EmitInstructionInt32(OpCodeNumber.Ldc_I4, aspect.Validators.Count);
                    writer.EmitInstructionType(OpCodeNumber.Newarr, module.Cache.GetIntrinsic(IntrinsicType.Object));
                    writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, fieldValuesArrayLocal);

                    int i = 0;
                    foreach (FieldValidationAttribute validator in aspect.Validators)
                    {
                        FieldDefDeclaration fieldDef = typeDef.Fields.GetByName(validator.TargetLocation.Name);
                        IField fieldSpec             = GenericHelper.GetFieldCanonicalGenericInstance(fieldDef);

                        writer.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, fieldValuesArrayLocal);
                        writer.EmitInstructionInt32(OpCodeNumber.Ldc_I4, i);
                        writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                        writer.EmitInstructionField(OpCodeNumber.Ldfld, fieldSpec);
                        writer.EmitConvertToObject(fieldSpec.FieldType);
                        writer.EmitInstruction(OpCodeNumber.Stelem_Ref);

                        i++;
                    }

                    // Get the validator method.
                    IMethod validateMethod = module.Cache.GetItem(
                        () => module.FindMethod(
                            typeof(TypeValidationAspect).GetMethod("Validate"),
                            BindingOptions.Default));

                    // Call the validator.
                    this.AspectWeaverInstance.AspectRuntimeInstanceField.EmitLoadField(writer, null);
                    writer.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, fieldValuesArrayLocal);
                    writer.EmitInstructionMethod(OpCodeNumber.Callvirt, validateMethod);

                    writer.EmitInstruction(OpCodeNumber.Ret);
                    writer.DetachInstructionSequence();
                }
            }
                public override void Implement(MethodBodyTransformationContext context)
                {
                    /* We want to generate the following
                     *  if ( !this.<>4__this.Dispatcher.CheckAccess() )
                     *   {
                     *      SynchronizationContext oldContext = SynchronizationContext.Current;
                     *      SynchronizationContext.SetSynchronizationContext( this.<>4__this.Dispatcher.SynchronizationContext );
                     *      this.<>t__dispatchAwaiter = Task.Yield().GetAwaiter();
                     *      this.<>t__builder.AwaitUnsafeOnCompleted<YieldAwaitable.YieldAwaiter, Player.<Ping>d__2>(ref  this.<>t__dispatchAwaiter, ref this);
                     *      SynchronizationContext.SetSynchronizationContext( oldContext );
                     *      return;
                     *   }
                     *
                     */



                    MethodDefDeclaration targetMethod = (MethodDefDeclaration)context.TargetElement;
                    TypeDefDeclaration   targetType   = targetMethod.DeclaringType;

                    targetMethod.MethodBody.MaxStack = -1;

                    // Add the field where we will store the awaiter.
                    FieldDefDeclaration awaiterFieldDef = new FieldDefDeclaration
                    {
                        Name       = "<>t__dispatchAwaiter",
                        FieldType  = this.parent.yieldAwaiter_Type,
                        Attributes = FieldAttributes.Private
                    };

                    targetType.Fields.Add(awaiterFieldDef);
                    IField awaiterField = awaiterFieldDef.GetCanonicalGenericInstance();

                    // Find other fields.
                    IField thisField    = targetType.Fields.Single <FieldDefDeclaration>(f => f.Name.EndsWith("__this")).GetCanonicalGenericInstance();
                    IField builderField = targetType.Fields.GetByName("<>t__builder").GetCanonicalGenericInstance();

                    // Emit instructions.
                    InstructionBlock myBlock    = context.InstructionBlock.AddChildBlock(null, NodePosition.After, null);
                    InstructionBlock theirBlock = context.InstructionBlock.AddChildBlock(null, NodePosition.After, null);

                    LocalVariableSymbol awaitableLocal = myBlock.DefineLocalVariable(this.parent.task_Yield_Method.ReturnType, "awaitable");
                    LocalVariableSymbol synchronizationContextLocal = myBlock.DefineLocalVariable(this.parent.synchronizationContext_Type, "oldContext");

                    InstructionSequence entrySequence = myBlock.AddInstructionSequence(null, NodePosition.After, null);
                    InstructionSequence exitSequence  = myBlock.AddInstructionSequence(null, NodePosition.After, null);
                    InstructionWriter   writer        = new InstructionWriter();

                    writer.AttachInstructionSequence(entrySequence);

                    // Emit: if ( this.<>4__this.Dispatcher.CheckAccess() ) goto exitSequence;
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstructionField(OpCodeNumber.Ldfld, thisField);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, this.parent.actor_GetDispatcher_Method);
                    writer.EmitInstructionMethod(OpCodeNumber.Callvirt, this.parent.dispatcher_CheckAccess_Method);
                    writer.EmitBranchingInstruction(OpCodeNumber.Brtrue, exitSequence);

                    // Emit: this.<>t__dispatchAwaiter = Task.Yield().GetAwaiter()
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, this.parent.task_Yield_Method);
                    writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, awaitableLocal);
                    writer.EmitInstructionLocalVariable(OpCodeNumber.Ldloca, awaitableLocal);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, this.parent.yieldAwaitable_GetAwaiter_Method);
                    writer.EmitInstructionField(OpCodeNumber.Stfld, awaiterField);

                    // Emit: oldContext = SynchronizationContext.Current
                    writer.EmitInstructionMethod(OpCodeNumber.Call, this.parent.synchronizationContext_getCurrent_Method);
                    writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, synchronizationContextLocal);

                    // Emit: SynchronizationContext.SetSynchronizationContext(this.<>4__this.Dispatcher.SynchronizationContext)
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstructionField(OpCodeNumber.Ldfld, thisField);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, this.parent.actor_GetDispatcher_Method);
                    writer.EmitInstructionMethod(OpCodeNumber.Callvirt, this.parent.dispatcher_getSynchronizationContext_Method);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, this.parent.synchronizationContext_SetSynchronizationContext_Method);

                    // Choose which AwaitUnsafeOnCompleted method to call.
                    IGenericMethodDefinition awaitUnsafeOnCompletedMethod;

                    ITypeSignature[] awaitUnsafeOnCompletedGenericTypeParameters;
                    if (builderField.FieldType == this.parent.asyncVoidMethodBuilder_Type)
                    {
                        awaitUnsafeOnCompletedMethod = this.parent.asyncVoidMethodBuilder_AwaitUnsafeOnCompleted_Method;
                        awaitUnsafeOnCompletedGenericTypeParameters = null;
                    }
                    else if (builderField.FieldType == this.parent.asyncTaskMethodBuilder_Type)
                    {
                        awaitUnsafeOnCompletedMethod = this.parent.asyncTaskMethodBuilder_AwaitUnsafeOnCompleted_Method;
                        awaitUnsafeOnCompletedGenericTypeParameters = null;
                    }
                    else
                    {
                        // This is a generic task.
                        awaitUnsafeOnCompletedMethod = this.parent.asyncTaskMethodBuilderGeneric_AwaitUnsafeOnCompleted_Method;
                        awaitUnsafeOnCompletedGenericTypeParameters =
                            builderField.FieldType.GetGenericContext(GenericContextOptions.None).GetGenericTypeParameters();
                    }

                    IMethod awaitUnsafeOnCompletedGenericMethod =
                        awaitUnsafeOnCompletedMethod.GetGenericInstance(new GenericMap(awaitUnsafeOnCompletedGenericTypeParameters,
                                                                                       new ITypeSignature[]
                    {
                        this.parent.yieldAwaiter_Type,
                        targetType.GetCanonicalGenericInstance()
                    }));

                    // Emit: this.<>t__builder.AwaitUnsafeOnCompleted<YieldAwaitable.YieldAwaiter, Player.<Ping>d__2>(ref  this.<>t__dispatchAwaiter, ref this);
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstructionField(OpCodeNumber.Ldflda, builderField);
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstructionField(OpCodeNumber.Ldflda, awaiterField);
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, awaitUnsafeOnCompletedGenericMethod);

                    // Emit: SynchronizationContext.SetSynchronizationContext( oldContext );
                    writer.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, synchronizationContextLocal);
                    writer.EmitInstructionMethod(OpCodeNumber.Call, this.parent.synchronizationContext_SetSynchronizationContext_Method);


                    writer.EmitBranchingInstruction(OpCodeNumber.Leave, context.LeaveBranchTarget);

                    writer.DetachInstructionSequence();

                    // We are done. Give the pipeline to the next node.
                    context.AddRedirection(theirBlock, context.LeaveBranchTarget);
                }