diff --git a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs index e98d4150e2..8c030aee6e 100644 --- a/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs +++ b/Orm/Xtensive.Orm/Orm/Model/TypeInfo.cs @@ -11,10 +11,13 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; +using System.Reflection; using JetBrains.Annotations; using Xtensive.Core; using Xtensive.Orm.Internals; using Xtensive.Orm.Validation; +using Xtensive.Orm.Upgrade; +using Xtensive.Reflection; using Xtensive.Tuples; using Xtensive.Tuples.Transform; using Tuple = Xtensive.Tuples.Tuple; @@ -40,6 +43,10 @@ public sealed class TypeInfo : SchemaMappedNode /// public const int MinTypeId = 100; + private static readonly Type + TypeEntity = typeof(Entity), + TypeStructure = typeof(Structure); + private static readonly ImmutableHashSet EmptyTypes = ImmutableHashSet.Create(); private readonly ColumnInfoCollection columns; @@ -67,7 +74,7 @@ public sealed class TypeInfo : SchemaMappedNode private IDictionary, FieldInfo> structureFieldMapping; private List overridenAssociations; private FieldInfo typeIdField; - + private TypeInfo ancestor; private IReadOnlySet ancestors; @@ -422,6 +429,9 @@ public FieldInfoCollection Fields get { return fields; } } + private FieldInfo[] persistentFields; + internal FieldInfo[] PersistentFields => persistentFields ??= BuildPersistentFields(); + /// /// Gets the field map for implemented interfaces. /// @@ -915,6 +925,67 @@ private IDictionary, FieldInfo> BuildStructureFieldMapping() return new ReadOnlyDictionary, FieldInfo>(result); } + private static IEnumerable GetBaseFields(Type type, IEnumerable fields, bool bRoot) + { + if (type == TypeEntity || type == TypeStructure) { + return Array.Empty(); + } + var declared = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .ToDictionary(p => p.Name, p => p.MetadataToken); + + return GetBaseFields(type.BaseType, fields, bRoot) + .Concat( + fields.Select(p => (p, declared.TryGetValue(p.UnderlyingProperty.Name, out var token) ? token : 0)) + .Where(t => bRoot ? t.Item2 != 0 : t.Item1.UnderlyingProperty.MetadataToken == t.Item2) + .OrderBy(t => t.Item2) + .Select(t => t.Item1) + ); + } + + private static bool IsOverrideOfVirtual(FieldInfo a, FieldInfo p) => + a.Name == p.Name + && a.Name != "TypeId" + && p.IsInherited + && a.UnderlyingProperty.GetMethod?.IsVirtual == true + && p.UnderlyingProperty.GetMethod?.IsVirtual == true; + + private FieldInfo[] BuildPersistentFields() + { + var propTypeId = IsEntity ? Fields[nameof(Entity.TypeId)] : null; + bool isRoot = Hierarchy?.Root == this; + var potentialFields = Fields.Where(p => !p.IsDynamicallyDefined && p.Parent == null && p != propTypeId).ToArray(); + var recycled = UnderlyingType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(p => p.GetAttribute() != null && !potentialFields.Any(pf => pf.UnderlyingProperty == p)); + + FieldInfo[] baseFields; + FieldInfo[] ancestorFields = Array.Empty(); + if (Ancestor != null && Ancestor.UnderlyingType != TypeStructure) { + ancestorFields = Ancestor.PersistentFields; + baseFields = ancestorFields.Select(p => p != null && Fields.TryGetValue(p.Name, out var f) ? f : null).ToArray(); + } + else { + baseFields = !(IsEntity || IsStructure) + ? Array.Empty() + : GetBaseFields(UnderlyingType.BaseType, potentialFields, isRoot).ToArray(); + } + var baseFieldsSet = baseFields.ToHashSet(); + var props = baseFields.Concat( + potentialFields.Where(p => p.UnderlyingProperty.DeclaringType == UnderlyingType + && (isRoot + || p.IsExplicit + || !baseFieldsSet.Contains(p) + || ancestorFields.Any(a => IsOverrideOfVirtual(a, p)))) + .Select(p => (p, p.UnderlyingProperty.MetadataToken)) + .Concat(recycled.Select(p => ((FieldInfo)null, p.MetadataToken))) + .OrderBy(t => t.Item2) + .Select(t => t.Item1) + ).Select(p => p?.ReflectedType.IsInterface == true ? FieldMap[p] : p); + if (IsEntity && Ancestor == null) { + props = props.Prepend(propTypeId); + } + return props.ToArray(); + } + #endregion /// diff --git a/Orm/Xtensive.Orm/Orm/Persistent.cs b/Orm/Xtensive.Orm/Orm/Persistent.cs index 3e48bca07f..2016332df8 100644 --- a/Orm/Xtensive.Orm/Orm/Persistent.cs +++ b/Orm/Xtensive.Orm/Orm/Persistent.cs @@ -165,6 +165,9 @@ protected internal object GetFieldValue(string fieldName) return GetFieldValue(TypeInfo.Fields[fieldName]); } + protected internal T GetFieldValue(int fieldIndex) => + GetNormalizedFieldValue(TypeInfo.PersistentFields[fieldIndex]); + /// /// Gets the field value. /// Field value type must be specified precisely. @@ -173,7 +176,10 @@ protected internal object GetFieldValue(string fieldName) /// Field value type. /// The field. /// Field value. - protected internal T GetFieldValue(FieldInfo field) + protected internal T GetFieldValue(FieldInfo field) => + GetNormalizedFieldValue(field.ReflectedType.IsInterface ? TypeInfo.FieldMap[field] : field); + + protected internal T GetNormalizedFieldValue(FieldInfo field) { if (field.ReflectedType.IsInterface) field = TypeInfo.FieldMap[field]; @@ -356,6 +362,9 @@ protected internal void SetFieldValue(string fieldName, object value) SetFieldValue(TypeInfo.Fields[fieldName], value); } + protected internal void SetFieldValue(int fieldIndex, T value) => + SetNormalizedFieldValue(TypeInfo.PersistentFields[fieldIndex], value, null, null); + /// /// Sets the field value. /// Field value type must be specified precisely. @@ -383,10 +392,11 @@ protected internal void SetFieldValue(FieldInfo field, object value) SetFieldValue(field, value, null, null); } - internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext) + internal void SetFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext) => + SetNormalizedFieldValue(field.ReflectedType.IsInterface ? TypeInfo.FieldMap[field] : field, value, syncContext, removalContext); + + internal void SetNormalizedFieldValue(FieldInfo field, object value, SyncContext syncContext, RemovalContext removalContext) { - if (field.ReflectedType.IsInterface) - field = TypeInfo.FieldMap[field]; SystemSetValueAttempt(field, value); var fieldAccessor = GetFieldAccessor(field); object oldValue = GetFieldValue(field); diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs index c2f7b1b611..4102b030d8 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ImportReferencesStage.cs @@ -25,6 +25,7 @@ public override ActionResult Execute(ProcessorContext context) } registry.OrmAssembly = ormAssembly; + var intType = context.TargetModule.TypeSystem.Int32; var stringType = context.TargetModule.TypeSystem.String; var voidType = context.TargetModule.TypeSystem.Void; @@ -59,13 +60,13 @@ public override ActionResult Execute(ProcessorContext context) var persistentGetter = new MethodReference("GetFieldValue", voidType, persistentType) {HasThis = true}; var getterType = new GenericParameter("!!T", persistentGetter); persistentGetter.ReturnType = getterType; - persistentGetter.Parameters.Add(new ParameterDefinition(stringType)); + persistentGetter.Parameters.Add(new ParameterDefinition(intType)); persistentGetter.GenericParameters.Add(getterType); registry.PersistentGetterDefinition = context.TargetModule.ImportReference(persistentGetter); var persistentSetter = new MethodReference("SetFieldValue", voidType, persistentType) {HasThis = true}; var setterType = new GenericParameter("!!T", persistentSetter); - persistentSetter.Parameters.Add(new ParameterDefinition(stringType)); + persistentSetter.Parameters.Add(new ParameterDefinition(intType)); persistentSetter.Parameters.Add(new ParameterDefinition(setterType)); persistentSetter.GenericParameters.Add(setterType); registry.PersistentSetterDefinition = context.TargetModule.ImportReference(persistentSetter); diff --git a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs index 5f8e0c016b..fc9d9659d2 100644 --- a/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs +++ b/Weaver/Xtensive.Orm.Weaver/Stages/ModifyPersistentTypesStage.cs @@ -4,6 +4,8 @@ // Created by: Denis Krjuchkov // Created: 2013.08.21 +using System; +using System.Collections.Generic; using System.Linq; using Mono.Cecil; using Xtensive.Orm.Weaver.Tasks; @@ -38,8 +40,8 @@ public override ActionResult Execute(ProcessorContext context) new[] {references.Entity, references.FieldInfo}, }; - propertyChecker = (context.Language==SourceLanguage.CSharp) - ? (IPersistentPropertyChecker) new CsPropertyChecker() + propertyChecker = (context.Language==SourceLanguage.CSharp) + ? (IPersistentPropertyChecker) new CsPropertyChecker() : new VbPropertyChecker(); foreach (var type in context.PersistentTypes) @@ -107,26 +109,44 @@ private void ProcessStructure(ProcessorContext context, TypeInfo type) context.WeavingTasks.Add(new AddAttributeTask(definition, context.References.StructureTypeAttributeConstructor)); } + private Dictionary GetPropertyToIndexMap(TypeInfo type) + { + if (type is null) { + return new(); + } + var r = GetPropertyToIndexMap(type.BaseType); + int idx = r.Count == 0 ? 0 : r.Values.Max() + 1; + if (idx == 0 && type.Kind == PersistentTypeKind.Entity) { + idx = 1; // for TypeId + } + foreach (var p in type.Properties.Values.Where(p => p.IsPersistent) + .OrderBy(p => p.Definition.MetadataToken.ToInt32())) { + r[p] = (p.IsOverride && p.BaseProperty.IsPersistent) + ? r[p.BaseProperty] // For overridden persistent property assign base property's index + : idx++; + } + return r; + } + private void ProcessFields(ProcessorContext context, TypeInfo type) { - foreach (var property in type.Properties.Values.Where(p => p.IsPersistent)) { - if (!propertyChecker.ShouldProcess(property, context)) - continue; + var typeDefinition = type.Definition; + var propertyToIndex = GetPropertyToIndexMap(type); - var typeDefinition = type.Definition; + foreach (var property in type.Properties.Values.Where(p => p.IsPersistent && propertyChecker.ShouldProcess(p, context))) { + var persistentIndex = propertyToIndex[property]; var propertyDefinition = property.Definition; - var persistentName = property.PersistentName ?? property.Name; // Backing field context.WeavingTasks.Add(new RemoveBackingFieldTask(typeDefinition, propertyDefinition)); // Getter context.WeavingTasks.Add(new ImplementFieldAccessorTask(AccessorKind.Getter, - typeDefinition, propertyDefinition, persistentName)); + typeDefinition, propertyDefinition, persistentIndex)); // Setter if (property.IsKey) context.WeavingTasks.Add(new ImplementKeySetterTask(typeDefinition, propertyDefinition)); else context.WeavingTasks.Add(new ImplementFieldAccessorTask(AccessorKind.Setter, - typeDefinition, propertyDefinition, persistentName)); + typeDefinition, propertyDefinition, persistentIndex)); if (property.PersistentName!=null) context.WeavingTasks.Add(new AddAttributeTask(propertyDefinition, context.References.OverrideFieldNameAttributeConstructor, property.PersistentName)); diff --git a/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs b/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs index 006a761d90..590bb25562 100644 --- a/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs +++ b/Weaver/Xtensive.Orm.Weaver/Tasks/ImplementFieldAccessorTask.cs @@ -15,7 +15,7 @@ internal sealed class ImplementFieldAccessorTask : WeavingTask { private readonly TypeDefinition type; private readonly PropertyDefinition property; - private readonly string persistentName; + private readonly int persistentIndex; private readonly AccessorKind kind; public override ActionResult Execute(ProcessorContext context) @@ -42,7 +42,7 @@ private void ImplementSetter(ProcessorContext context) body.Instructions.Clear(); var il = body.GetILProcessor(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldstr, persistentName); + il.Emit(OpCodes.Ldc_I4, persistentIndex); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Call, accessor); il.Emit(OpCodes.Ret); @@ -56,7 +56,7 @@ private void ImplementGetter(ProcessorContext context) body.Instructions.Clear(); var il = body.GetILProcessor(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldstr, persistentName); + il.Emit(OpCodes.Ldc_I4, persistentIndex); il.Emit(OpCodes.Call, accessor); il.Emit(OpCodes.Ret); } @@ -75,7 +75,7 @@ private MethodReference GetAccessor(ProcessorContext context, return result; } - public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, PropertyDefinition property, string persistentName) + public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, PropertyDefinition property, int persistentIndex) { if (type==null) throw new ArgumentNullException("type"); @@ -85,7 +85,7 @@ public ImplementFieldAccessorTask(AccessorKind kind, TypeDefinition type, Proper this.kind = kind; this.type = type; this.property = property; - this.persistentName = persistentName; + this.persistentIndex = persistentIndex; } } } \ No newline at end of file