private void InjectEqualsObject(TypeDefDeclaration enhancedType, StructuralEqualityAttribute config,
                                        MethodDefDeclaration typedEqualsMethod)
        {
            var existingMethod = enhancedType.Methods.FirstOrDefault <IMethod>(declaration =>
            {
                return(declaration.Name == "Equals" &&
                       declaration.IsPublic() &&
                       !declaration.IsStatic &&
                       declaration.ParameterCount == 1 &&
                       declaration.GetParameterType(0).Equals(this.objectType));
            });

            if (existingMethod != null)
            {
                return;
            }

            // public virtual bool Equals( object other )
            var equalsDeclaration = new MethodDefDeclaration
            {
                Name              = "Equals",
                Attributes        = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConvention = CallingConvention.HasThis
            };

            enhancedType.Methods.Add(equalsDeclaration);
            equalsDeclaration.Parameters.Add(new ParameterDeclaration(0, "other", this.objectType));
            equalsDeclaration.ReturnParameter = ParameterDeclaration.CreateReturnParameter(this.booleanType);
            CompilerGeneratedAttributeHelper.AddCompilerGeneratedAttribute(equalsDeclaration);

            using (var writer = InstructionWriter.GetInstance())
            {
                var methodBody = MethodBodyCreator.CreateModifiableMethodBody(writer, equalsDeclaration);

                writer.AttachInstructionSequence(methodBody.PrincipalBlock.AddInstructionSequence());

                writer.EmitInstruction(OpCodeNumber.Ldc_I4_0);
                writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, methodBody.ReturnVariable);

                this.InjectReferenceEquals(writer, methodBody, enhancedType);

                var genericTypeInstance = enhancedType.GetCanonicalGenericInstance();

                this.EmitTypeCheck(enhancedType, config, writer, genericTypeInstance, methodBody);
                if (enhancedType.IsValueTypeSafe() == true)
                {
                    this.EmitEqualsObjectOfValueType(writer, genericTypeInstance, typedEqualsMethod, methodBody);
                }
                else
                {
                    this.EmitEqualsObjectOfReferenceType(writer, genericTypeInstance, typedEqualsMethod, methodBody);
                }

                writer.DetachInstructionSequence();
            }
        }
Ejemplo n.º 2
0
        private void ReplaceOperator(TypeDefDeclaration enhancedType, MethodDefDeclaration equalityMethodDef,
                                     bool negate)
        {
            InstructionBlock originalCode = equalityMethodDef.MethodBody.RootInstructionBlock;

            originalCode.Detach();

            InstructionBlock root = equalityMethodDef.MethodBody.CreateInstructionBlock();

            equalityMethodDef.MethodBody.RootInstructionBlock = root;
            var newSequence = root.AddInstructionSequence();

            using (var writer = InstructionWriter.GetInstance())
            {
                writer.AttachInstructionSequence(newSequence);

                if (enhancedType.IsValueType())
                {
                    var canonicalType = enhancedType.GetCanonicalGenericInstance();
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstructionType(OpCodeNumber.Box, canonicalType);
                    writer.EmitInstruction(OpCodeNumber.Ldarg_1);
                    writer.EmitInstructionType(OpCodeNumber.Box, canonicalType);
                }
                else
                {
                    writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                    writer.EmitInstruction(OpCodeNumber.Ldarg_1);
                }

                writer.EmitInstructionMethod(OpCodeNumber.Call, this.staticEqualsMethod);

                if (negate)
                {
                    writer.EmitInstruction(OpCodeNumber.Ldc_I4_0);
                    writer.EmitInstruction(OpCodeNumber.Ceq);
                }

                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);
                }
        private MethodDefDeclaration InjectEqualsType(TypeDefDeclaration enhancedType,
                                                      StructuralEqualityAttribute config, ICollection <FieldDefDeclaration> ignoredFields)
        {
            IType genericTypeInstance = enhancedType.GetCanonicalGenericInstance();

            var existingMethod = enhancedType.Methods.FirstOrDefault <IMethod>(declaration =>
            {
                return(declaration.Name == "Equals" &&
                       declaration.IsPublic() &&
                       !declaration.IsStatic &&
                       declaration.ParameterCount == 1 &&
                       declaration.GetParameterType(0).Equals(genericTypeInstance));
            });

            if (existingMethod != null)
            {
                return(existingMethod.GetMethodDefinition());
            }

            // public virtual bool Equals( Typed other )
            var equalsDeclaration = new MethodDefDeclaration
            {
                Name              = "Equals",
                Attributes        = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                CallingConvention = CallingConvention.HasThis
            };

            enhancedType.Methods.Add(equalsDeclaration);
            equalsDeclaration.Parameters.Add(new ParameterDeclaration(0, "other", genericTypeInstance));
            equalsDeclaration.ReturnParameter = ParameterDeclaration.CreateReturnParameter(this.booleanType);
            CompilerGeneratedAttributeHelper.AddCompilerGeneratedAttribute(equalsDeclaration);

            using (var writer = InstructionWriter.GetInstance())
            {
                var methodBody = MethodBodyCreator.CreateModifiableMethodBody(writer, equalsDeclaration);
                writer.AttachInstructionSequence(methodBody.PrincipalBlock.AddInstructionSequence());

                writer.EmitInstruction(OpCodeNumber.Ldc_I4_0);
                writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, methodBody.ReturnVariable);

                this.InjectReferenceEquals(writer, methodBody, enhancedType);
                // Writer is either attached to the same sequence (value types) or to a new one which should check the structure.

                // return base.Equals(other) && this.field1 == other.field1 && ...;
                if (!config.IgnoreBaseClass && enhancedType.IsValueTypeSafe() != true)
                {
                    // Find the base method.
                    var baseEqualsMethod = this.instanceEqualsMethod.FindOverride(enhancedType.BaseTypeDef, true)
                                           .GetInstance(enhancedType.Module, enhancedType.BaseType.GetGenericContext());

                    // Do not invoke object.Equals();
                    if (baseEqualsMethod.DeclaringType.GetTypeDefinition() != this.objectTypeDef)
                    {
                        writer.EmitInstruction(OpCodeNumber.Ldarg_0);
                        writer.EmitInstruction(OpCodeNumber.Ldarg_1);
                        writer.EmitInstructionMethod(OpCodeNumber.Call, baseEqualsMethod);

                        // base.Equals(other) returned false, go to return.
                        writer.EmitBranchingInstruction(OpCodeNumber.Brfalse, methodBody.ReturnSequence);
                    }
                }

                foreach (var field in GetFieldsForComparison(enhancedType, ignoredFields))
                {
                    this.EmitEqualsField(writer, methodBody, field);
                }

                InjectCustomMethods(enhancedType, writer, methodBody);

                // return true;
                writer.EmitInstruction(OpCodeNumber.Ldc_I4_1);
                writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, methodBody.ReturnVariable);
                writer.EmitBranchingInstruction(OpCodeNumber.Br, methodBody.ReturnSequence);
                writer.DetachInstructionSequence();
            }

            return(equalsDeclaration);
        }
        private void AddEquatable(TypeDefDeclaration enhancedType)
        {
            var genericInstance = this.equatableInterface.GetTypeDefinition().GetGenericInstance(new GenericMap(
                                                                                                     enhancedType.Module,
                                                                                                     new ITypeSignature[] { enhancedType.GetCanonicalGenericInstance() }));
            ITypeSignature interfaceRef = genericInstance.TranslateType(enhancedType.Module);

            enhancedType.InterfaceImplementations.Add(interfaceRef);
        }