Beispiel #1
0
            private MethodDefinition OverrideGetHashCode(
                TypeDefinition caseClassTypeDefinition,
                IEnumerable <CaseClassProperty> properties,
                FieldVarietyDetector fieldVarietyDetector)
            {
                var method = new MethodDefinition(
                    nameof(GetHashCode),
                    MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                    _typeSystem.Int32Reference);

                var getHashCodeMethod = _moduleDefinition.ImportReference(
                    _typeSystem.ObjectDefinition.Methods.Single(x => x.Name == nameof(GetHashCode)));
                var getHashCodeOfValueTypeMethod = _moduleDefinition.ImportReference(
                    _findType(typeof(ValueType).FullName).Methods.Single(m => m.Name == nameof(GetHashCode)));

                var codeEmitter = method.Body.GetILProcessor();

                codeEmitter.Emit(OpCodes.Ldc_I4, 857327845);

                foreach (var property in properties)
                {
                    codeEmitter.Emit(OpCodes.Ldc_I4, -1521134295);
                    codeEmitter.Emit(OpCodes.Mul);

                    var doAddition = Instruction.Create(OpCodes.Add);
                    if (fieldVarietyDetector.Detect(property.BackingFieldReference.FieldType).IsReferenceType ?? true)
                    {
                        var callGetHashCode = Instruction.Create(OpCodes.Callvirt, getHashCodeMethod);
                        property.BoxFieldValue(codeEmitter);
                        codeEmitter.Emit(OpCodes.Dup);
                        codeEmitter.Emit(OpCodes.Brtrue_S, callGetHashCode);
                        codeEmitter.Emit(OpCodes.Pop);
                        codeEmitter.Emit(OpCodes.Ldc_I4_0);
                        codeEmitter.Emit(OpCodes.Br_S, doAddition);
                        codeEmitter.Append(callGetHashCode);
                    }
                    else
                    {
                        codeEmitter.Emit(OpCodes.Ldarg_0);
                        codeEmitter.Emit(OpCodes.Ldfld, property.BackingFieldReference);
                        codeEmitter.Emit(OpCodes.Call, getHashCodeOfValueTypeMethod);
                    }

                    codeEmitter.Append(doAddition);
                }

                codeEmitter.Emit(OpCodes.Ret);

                return(method);
            }
Beispiel #2
0
        public override void Execute()
        {
            var fieldVarietyDetector = new FieldVarietyDetector(new WellKnownTypes(ModuleDefinition, FindType));

            foreach (var(caseClassesFactory, factoryMethods) in GetAllCaseClassFactoryMethodsGroupedByOwningClass())
            {
                var caseClassBuilder = new CaseClassBuilder(caseClassesFactory, ModuleDefinition, TypeSystem, FindType);
                foreach (var factoryMethod in factoryMethods)
                {
                    var caseClassTypeDefinition = caseClassBuilder.BuildCaseClass(factoryMethod, fieldVarietyDetector);
                    ModuleDefinition.Types.Add(caseClassTypeDefinition);
                    AdjustFactoryMethod(factoryMethod, caseClassTypeDefinition);
                }
            }
        }
Beispiel #3
0
            public TypeDefinition BuildCaseClass(MethodDefinition factoryMethod, FieldVarietyDetector fieldVarietyDetector)
            {
                var caseClassTypeDefinition = new TypeDefinition(
                    _factoryType.Namespace,
                    BuildCaseClassName(factoryMethod),
                    _factoryType.Attributes & (TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask) | TypeAttributes.Sealed,
                    _factoryType);

                caseClassTypeDefinition.GenericParameters.AddRange(
                    factoryMethod.GenericParameters
                    .Select(genericParameter => genericParameter.CloneWith(caseClassTypeDefinition)));

                var genericInstanceType = factoryMethod.GenericParameters.Any()
                        ? caseClassTypeDefinition.MakeGenericInstanceType(caseClassTypeDefinition.GenericParameters)
                        : null;

                var properties = factoryMethod.Parameters.Select((p, i) =>
                                                                 new CaseClassProperty(
                                                                     p,
                                                                     i,
                                                                     genericInstanceType,
                                                                     caseClassTypeDefinition.GenericParameters,
                                                                     _deepEqualityComparer,
                                                                     _moduleDefinition)).ToArray();

                caseClassTypeDefinition.Fields.AddRange(properties.Select(p => p.BackingField));
                caseClassTypeDefinition.Properties.AddRange(properties.Select(p => p.Property));
                caseClassTypeDefinition.Methods.AddRange(properties.Select(p => p.PropertyGetter));
                caseClassTypeDefinition.Methods.Add(GenerateConstructor(properties));
                caseClassTypeDefinition.Methods.Add(OverrideToString(caseClassTypeDefinition, properties));

                var typedEqualsMethod = ImplementIEquatable(caseClassTypeDefinition, properties, fieldVarietyDetector);

                caseClassTypeDefinition.Methods.Add(typedEqualsMethod);
                caseClassTypeDefinition.Methods.Add(OverrideObjectEquals(caseClassTypeDefinition, typedEqualsMethod));
                caseClassTypeDefinition.Methods.Add(OverrideGetHashCode(caseClassTypeDefinition, properties, fieldVarietyDetector));
                caseClassTypeDefinition.Methods.Add(MakeEqualityOperator(caseClassTypeDefinition, true));
                caseClassTypeDefinition.Methods.Add(MakeEqualityOperator(caseClassTypeDefinition, false));

                return(caseClassTypeDefinition);
            }
Beispiel #4
0
            private MethodDefinition ImplementIEquatable(
                TypeDefinition caseClassTypeDefinition,
                CaseClassProperty[] properties,
                FieldVarietyDetector fieldVarietyDetector)
            {
                var concreteCaseClassType = fieldVarietyDetector.GenericIEquatable.MarkAsImplementedBy(caseClassTypeDefinition);

                var method = new MethodDefinition(
                    nameof(IEquatable <object> .Equals),
                    MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                    _typeSystem.BooleanReference);

                var otherInstance = new ParameterDefinition("other", ParameterAttributes.None, concreteCaseClassType);

                method.Parameters.Add(otherInstance);
                var compareCodeEmitter = method.Body.GetILProcessor();

                // if (ReferenceEquals(other, null)) return false;
                var keepAnalyzingProperties = Instruction.Create(OpCodes.Nop);

                compareCodeEmitter.Emit(OpCodes.Ldarg_1);
                compareCodeEmitter.Emit(OpCodes.Ldnull);
                compareCodeEmitter.Emit(OpCodes.Ceq);
                compareCodeEmitter.Emit(OpCodes.Brfalse_S, keepAnalyzingProperties);
                compareCodeEmitter.Emit(OpCodes.Ldc_I4_0);
                compareCodeEmitter.Emit(OpCodes.Ret);
                compareCodeEmitter.Append(keepAnalyzingProperties);

                foreach (var property in properties)
                {
                    property.EmitEqualityCheck(compareCodeEmitter, fieldVarietyDetector);
                }

                compareCodeEmitter.Emit(OpCodes.Ldc_I4_1);
                compareCodeEmitter.Emit(OpCodes.Ret);

                return(method);
            }
Beispiel #5
0
            public void EmitEqualityCheck(ILProcessor compareCodeEmitter, FieldVarietyDetector fieldVarietyDetector)
            {
                switch (fieldVarietyDetector.Detect(BackingFieldReference.FieldType))
                {
                case ReferenceTypeImplementingIEquatable referenceTypeImplementingIEquatable:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.EquatableReferencesAreEqual),
                        referenceTypeImplementingIEquatable.FieldType);
                    break;

                case TypedCollection typedCollection:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.TypedCollectionsAreEqual),
                        typedCollection.ElementType);
                    break;

                case UntypedColection _:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.UntypedCollectionsAreEqual));
                    break;

                case PlainReferenceType plainReferenceType:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.ReferenceInstancesAreEqual),
                        plainReferenceType.FieldType);
                    break;

                case ValueTypeImplementingIEquatable valueTypeImplementingIEquatable:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.EquatableValuesAreEqual),
                        valueTypeImplementingIEquatable.FieldType);
                    break;

                case PlainValueType plainValueType:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.ValueInstancesAreEqual),
                        plainValueType.FieldType);
                    break;

                case NullableTypeImplementingIEquatable nullableTypeImplementingIEquatable:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.EquatableNullablesAreEqual),
                        nullableTypeImplementingIEquatable.UnderlyingType);
                    break;

                case PlainNullableType plainNullableType:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.NullablesAreEqual),
                        plainNullableType.UnderlyingType);
                    break;

                case GenericTypeImplementingIEquatable genericTypeImplementingIEquatable:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.EquatableGenericsAreEqual),
                        genericTypeImplementingIEquatable.GenericParameter);
                    break;

                case PlainGenericType plainGenericType:
                    EmitFieldEqualityTestingCode(
                        compareCodeEmitter,
                        nameof(DeepEqualityComparer.GenericsAreEqual),
                        plainGenericType.GenericParameter);
                    break;

                case FieldVariety fieldVariety:
                    throw new InvalidOperationException($"Program logic exception: no code to handle {fieldVariety.GetType()} field variety.");
                }
            }