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);
            }
Beispiel #3
0
        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);
        }