private static void InjectCustomMethods(TypeDefDeclaration enhancedType, InstructionWriter writer, CreatedEmptyMethod methodBody) { // Custom equality methods. foreach (var customEqualsMethod in enhancedType.Methods) { if (customEqualsMethod.CustomAttributes.GetOneByType( "PostSharp.Community.StructuralEquality.AdditionalEqualsMethodAttribute") != null) { if (customEqualsMethod.IsStatic || !customEqualsMethod.ReturnParameter.ParameterType.IsIntrinsic(IntrinsicType.Boolean) || customEqualsMethod.Parameters.Count != 1 || !customEqualsMethod.Parameters[0].ParameterType.GetTypeDefinition().Equals(enhancedType) ) { CustomMethodSignatureError(enhancedType, customEqualsMethod); } writer.EmitInstruction(OpCodeNumber.Ldarg_0); writer.EmitInstruction(OpCodeNumber.Ldarg_1); writer.EmitInstructionMethod(OpCodeNumber.Call, customEqualsMethod.GetCanonicalGenericInstance()); writer.EmitBranchingInstruction(OpCodeNumber.Brfalse, methodBody.ReturnSequence); } } }
private void EmitEqualsField(InstructionWriter writer, CreatedEmptyMethod methodBody, FieldDefDeclaration field) { ITypeSignature fieldType = field.FieldType; if ((fieldType.GetNakedType() is IntrinsicTypeSignature sig && sig.IntrinsicType != IntrinsicType.Object && sig.IntrinsicType != IntrinsicType.String ) || fieldType.IsEnum()) { this.EmitSimpleValueCheck(writer, methodBody, field); }
public void AddGetHashCodeTo(TypeDefDeclaration enhancedType, StructuralEqualityAttribute config, ISet <FieldDefDeclaration> ignoredFields) { if (enhancedType.Methods.Any <IMethod>(m => m.Name == "GetHashCode" && m.ParameterCount == 0)) { // GetHashCode already present, just keep it. return; } // Create signature MethodDefDeclaration method = new MethodDefDeclaration { Name = "GetHashCode", CallingConvention = CallingConvention.HasThis, Attributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig }; enhancedType.Methods.Add(method); CompilerGeneratedAttributeHelper.AddCompilerGeneratedAttribute(method); method.ReturnParameter = ParameterDeclaration.CreateReturnParameter(enhancedType.Module.Cache.GetIntrinsic(IntrinsicType.Int32)); // Generate ReSharper-style Fowler–Noll–Vo hash: using (InstructionWriter writer = InstructionWriter.GetInstance()) { CreatedEmptyMethod getHashCodeData = MethodBodyCreator.CreateModifiableMethodBody(writer, method); var resultVariable = getHashCodeData.ReturnVariable; writer.AttachInstructionSequence(getHashCodeData.PrincipalBlock.AddInstructionSequence()); // Start with 0 writer.EmitInstruction(OpCodeNumber.Ldc_I4_0); writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, resultVariable); bool first = true; // Add base.GetHashCode(): if (!config.IgnoreBaseClass) { bool ignorable = enhancedType.BaseTypeDef.Name == "System.Object" || enhancedType.IsValueTypeSafe() == true; if (!ignorable) { var baseHashCode = project.Module.FindMethod(enhancedType.BaseTypeDef, "GetHashCode", BindingOptions.DontThrowException, 0); // TODO Gael says: using FindOverride would be better if (baseHashCode != null) { writer.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, resultVariable); writer.EmitInstruction(OpCodeNumber.Ldarg_0); // TODO what if it is two steps removed? then we won't call it! writer.EmitInstructionMethod(OpCodeNumber.Call, baseHashCode.GetGenericInstance(enhancedType.BaseType.GetGenericContext())); writer.EmitInstruction(OpCodeNumber.Add); writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, resultVariable); first = false; } } } // For each field, do "hash = hash * 397 ^ field?.GetHashCode(); foreach (FieldDefDeclaration field in enhancedType.Fields) { if (field.IsConst || field.IsStatic || ignoredFields.Contains(field)) { continue; } this.AddFieldCode(field, first, writer, resultVariable, method, enhancedType); first = false; } // Now custom logic: foreach (var customLogic in enhancedType.Methods) { if (customLogic.CustomAttributes.GetOneByType(typeof(AdditionalGetHashCodeMethodAttribute) .FullName) != null) { writer.EmitInstructionLocalVariable(OpCodeNumber.Ldloc, resultVariable); writer.EmitInstructionInt32(OpCodeNumber.Ldc_I4, magicNumber); writer.EmitInstruction(OpCodeNumber.Mul); AddCustomLogicCall(enhancedType, writer, customLogic); writer.EmitInstruction(OpCodeNumber.Xor); writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, resultVariable); } } // Return the hash: writer.EmitBranchingInstruction(OpCodeNumber.Br, getHashCodeData.ReturnSequence); writer.DetachInstructionSequence(); } }
private void EmitEqualsObjectOfValueType(InstructionWriter writer, IType genericTypeInstance, MethodDefDeclaration typedEqualsMethod, CreatedEmptyMethod methodBody) { // return this.Equals((Typed)other); writer.EmitInstruction(OpCodeNumber.Ldarg_0); writer.EmitInstruction(OpCodeNumber.Ldarg_1); writer.EmitInstructionType(OpCodeNumber.Unbox_Any, genericTypeInstance); writer.EmitInstructionMethod(OpCodeNumber.Call, typedEqualsMethod.GetCanonicalGenericInstance()); writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, methodBody.ReturnVariable); writer.EmitBranchingInstruction(OpCodeNumber.Br, methodBody.ReturnSequence); }
private void EmitEqualsObjectOfReferenceType(InstructionWriter writer, IType genericTypeInstance, MethodDefDeclaration typedEqualsMethod, CreatedEmptyMethod methodBody) { // Go to typed check. writer.EmitInstruction(OpCodeNumber.Ldarg_0); writer.EmitInstruction(OpCodeNumber.Ldarg_1); writer.EmitInstructionType(OpCodeNumber.Castclass, genericTypeInstance); writer.EmitInstructionMethod(OpCodeNumber.Call, typedEqualsMethod.GetCanonicalGenericInstance()); writer.EmitInstructionLocalVariable(OpCodeNumber.Stloc, methodBody.ReturnVariable); writer.EmitBranchingInstruction(OpCodeNumber.Br, methodBody.ReturnSequence); }
private void EmitTypeCheck(TypeDefDeclaration enhancedType, StructuralEqualityAttribute config, InstructionWriter writer, IType genericTypeInstance, CreatedEmptyMethod methodBody) { switch (config.TypeCheck) { case TypeCheck.ExactlyTheSameTypeAsThis: this.InjectExactlyTheSameTypeAsThis(writer, enhancedType, genericTypeInstance); break; case TypeCheck.ExactlyOfType: this.InjectExactlyOfType(writer, genericTypeInstance); break; case TypeCheck.SameTypeOrSubtype: this.InjectSameTypeOrSubtype(writer, genericTypeInstance); break; default: throw new InjectionException("EQU4", $"Unknown TypeCheck value: {config.TypeCheck}"); } // Types are different, return false. writer.EmitBranchingInstruction(OpCodeNumber.Brfalse, methodBody.ReturnSequence); }