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(); } }
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); }