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); }
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); } } }
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); }
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); }
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."); } }