protected override void VisitProperty <TContainer, TValue>(Property <TContainer, TValue> property, ref TContainer container, ref TValue value) { if (!RuntimeTypeInfoCache.IsContainerType(value.GetType())) { return; } var isWrapper = typeof(PropertyWrapper <TValue>).IsInstanceOfType(container); if (!isWrapper) { AddToPath(property); } try { var path = GetCurrentPath(); using (var references = VisitorContext.MakeVisitedReferencesScope(this, ref value, path)) { if (references.VisitedOnCurrentBranch) { VisitorContext.Parent.Add(new CircularReferenceElement <TValue>(VisitorContext.Root, property, value, path, references.GetReferencePath())); return; } var inspector = default(IInspector); if (!path.Empty || EnableRootCustomInspectors) { var visitor = new CustomInspectorVisitor <TValue>(); visitor.PropertyPath = path; visitor.Root = VisitorContext.Root; visitor.Property = property; PropertyContainer.Visit(ref value, visitor); inspector = visitor.Inspector; } var old = EnableRootCustomInspectors; EnableRootCustomInspectors = true; if (null != inspector) { var customInspector = new CustomInspectorElement(path, inspector, VisitorContext.Root); VisitorContext.Parent.contentContainer.Add(customInspector); } else { RecurseProperty(ref value, property, path); } EnableRootCustomInspectors = old; } } finally { if (!isWrapper) { RemoveFromPath(property); } } }
public override VisualElement Build() { var root = new VisualElement(); var attributes = $"[{string.Join(", ", Target.Descriptor.GetAttributes().Select(a => TypeUtility.GetTypeDisplayName(a.GetType())))}]"; if (attributes.Length > 2) { var attributesLabel = new Label(attributes); Resources.Templates.Explorer.Property.AddStyles(attributesLabel); attributesLabel.AddToClassList(UssClasses.Unity.BaseFieldLabel); attributesLabel.AddToClassList(k_Attributes); root.contentContainer.Add(attributesLabel); } var descriptor = new AttributeDescriptorElement <string, string, Label, Label>(new AttributeDescriptor <string, string>(Target.Descriptor.Name, TypeUtility.GetTypeDisplayName(Target.Value))); var type = Target.Descriptor.DeclaredValueType(); if (RuntimeTypeInfoCache.IsContainerType(type) && !type.IsAbstract && !type.IsInterface) { descriptor.ShowValueAsLink(); } descriptor.RegisterCallback <TypeSelectedEvent <AttributeDescriptor <string, string> > >(evt => { GetContext <Explorer.Context>().SelectType(Target.Value); }); root.contentContainer.Add(descriptor); return(root); }
public static void Visit <TContainer, TVisitor>(ref TContainer container, TVisitor visitor, ref ChangeTracker changeTracker) where TVisitor : IPropertyVisitor { if (RuntimeTypeInfoCache <TContainer> .IsAbstractOrInterface()) { PropertyBagResolver.Resolve(container.GetType())?.Accept(container, visitor, ref changeTracker); } else { PropertyBagResolver.Resolve <TContainer>()?.Accept(ref container, visitor, ref changeTracker); } }
public static void Visit <TContainer, TVisitor>(ref TContainer container, TVisitor visitor, ref ChangeTracker changeTracker) where TVisitor : IPropertyVisitor { if (!RuntimeTypeInfoCache <TContainer> .IsValueType() && null != container && typeof(TContainer) != container.GetType()) { var boxed = (object)container; PropertyBagResolver.Resolve(container.GetType())?.Accept(ref boxed, visitor, ref changeTracker); container = (TContainer)boxed; } else { PropertyBagResolver.Resolve <TContainer>()?.Accept(ref container, visitor, ref changeTracker); } }
public static void Transfer <TDestination, TSource>(ref TDestination destination, ref TSource source, ref ChangeTracker changeTracker) { if (RuntimeTypeInfoCache <TSource> .IsAbstractOrInterface()) { var propertyBag = PropertyBagResolver.Resolve(source.GetType()); var action = new TransferAbstractType <TDestination> { Destination = destination, SourceContainer = (object)source }; propertyBag.Cast(action); destination = action.Destination; } else { Visit(ref destination, new TransferVisitor <TSource>(source), ref changeTracker); } }
void VisitWithCustomInspectorOrDefault <TContainer, TDeclaredType>(IProperty property, ref TContainer container, ref TDeclaredType value, DefaultVisitHandler <TDeclaredType> defaultVisit) { if (!RuntimeTypeInfoCache.IsContainerType(value.GetType())) { return; } var isWrapper = typeof(PropertyWrapper <TDeclaredType>).IsInstanceOfType(container); if (!isWrapper) { AddToPath(property); } try { var path = GetCurrentPath(); using (var references = VisitorContext.MakeVisitedReferencesScope(this, ref value, path)) { if (references.VisitedOnCurrentBranch) { VisitorContext.Parent.Add(new CircularReferenceElement <TDeclaredType>(VisitorContext.Root, property, value, path, references.GetReferencePath())); return; } if (TryVisitWithCustomInspector(ref value, path, property)) { return; } defaultVisit(property, ref value, path); } } finally { if (!isWrapper) { RemoveFromPath(property); } } }
/// <summary> /// Register a reflected property bag which is compatible with scene serialization for the given type and the /// types of its properties, and their properties recursively /// </summary> /// <param name="type">The type which will used to create the property bags</param> public static void RegisterPropertyBagRecursively(Type type) { if (!RuntimeTypeInfoCache.IsContainerType(type) || type.IsGenericTypeDefinition || type.IsAbstract || type.IsInterface) { return; } var propertyBag = ReflectedPropertyBagProvider.Instance.CreatePropertyBag(type); if (propertyBag == null) { return; } propertyBag.Register(); var method = SerializationUtils.GetRegisterPropertyBagsForPropertiesMethod(type); k_RegisterPropertyBagsArguments[0] = propertyBag; method?.Invoke(null, k_RegisterPropertyBagsArguments); }
VisitStatus IVisitAdapter.Visit <TProperty, TContainer, TValue>( IPropertyVisitor visitor, TProperty property, ref TContainer container, ref TValue value, ref ChangeTracker changeTracker) { if (!typeof(TValue).IsEnum) { return(VisitStatus.Unhandled); } if (RuntimeTypeInfoCache <TValue> .IsFlagsEnum()) { GuiFactory.FlagsField(property, ref container, ref value, VisitorContext); } else { GuiFactory.EnumField(property, ref container, ref value, VisitorContext); } return(VisitStatus.Override); }
internal static void Construct <TValue>(ref TValue value, ISerializedTypeProvider provider) { if (RuntimeTypeInfoCache <TValue> .IsValueType) { if (!(RuntimeTypeInfoCache <TValue> .IsNullable && RuntimeTypeInfoCache.IsContainerType(Nullable.GetUnderlyingType(typeof(TValue))))) { return; } } var serializedType = provider.GetSerializedType(); if (null != serializedType) { if (!typeof(TValue).IsAssignableFrom(serializedType)) { throw new ArgumentException($"Type mismatch. DeclaredType=[{typeof(TValue)}] SerializedType=[{serializedType}]"); } ConstructFromSerializedType(ref value, serializedType, provider); return; } if (RuntimeTypeInfoCache <TValue> .IsObjectType && null == value) { value = (TValue)provider.GetDefaultObject(); return; } if (RuntimeTypeInfoCache <TValue> .IsAbstractOrInterface) { throw new ArgumentException(); } ConstructFromDeclaredType(ref value, provider); }
void CloneValue <TValue>(ref TValue dstValue, TValue srcValue) { // Values types can be copied as-is. if (!RuntimeTypeInfoCache <TValue> .IsContainerType) { dstValue = srcValue; return; } if (RuntimeTypeInfoCache <TValue> .CanBeNull && null == srcValue) { dstValue = default; return; } if (RuntimeTypeInfoCache <TValue> .IsValueType) { dstValue = default; } else { var type = srcValue.GetType(); #if !UNITY_DOTSRUNTIME // UnityEngine references can be copied as-is. if (typeof(UnityEngine.Object).IsAssignableFrom(type)) { dstValue = srcValue; return; } #endif // Boxed value types can be copied as-is. if (!RuntimeTypeInfoCache.IsContainerType(type)) { dstValue = srcValue; return; } var isReferenceType = !(type.IsValueType || type == typeof(string)); if (isReferenceType) { // If we have already encountered this object before. Use the already created instance. // This is to support things like circular references/graph structures. // The root object is not added to the map so we need to explicitly check for it. if (m_RootSource == (object)srcValue) { dstValue = (TValue)m_RootDestination; return; } // Otherwise let's check the map. if (null != m_References && m_References.TryGetValue(srcValue, out var existingReference)) { dstValue = (TValue)existingReference; return; } } // We are dealing with an arbitrary csharp object. // We may or may not need to construct a new instance depending on the default constructor of the container. // If we already have a use-able instance we will use that instead of creating a new one. // This is both for performance and to handle init-only fields. if (type.IsArray) { var count = srcValue is IList srcList ? srcList.Count : 0; if (null == dstValue || (dstValue as Array)?.Length != count) { dstValue = (TValue)(object)Array.CreateInstance(type.GetElementType(), count); } } else { if (null == dstValue || dstValue.GetType() != type) { dstValue = (TValue)Activator.CreateInstance(type); } } // Retain a mapping of references within this object. This is needed to support things like circular references. if (isReferenceType) { if (null == m_References) { m_References = new Dictionary <object, object>(); } m_References.Add(srcValue, dstValue); } } // We now have a properly constructed instance of the dstValue, nest in and copy all members. // Unity.Properties will automatically handle collection type from this point. var dstContainer = m_Stack; m_Stack = dstValue; PropertyContainer.Visit(ref srcValue, this, out _); dstValue = (TValue)m_Stack; m_Stack = dstContainer; }
internal void WriteValueWithoutAdapters <TValue>(TValue value, bool isRoot) { if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.WritePrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } if (RuntimeTypeInfoCache <TValue> .CanBeNull && null == value) { m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsMultidimensionalArray) { // No support for multidimensional arrays yet. This can be done using adapters for now. m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsNullable) { m_Stream->Add(k_TokenNone); var underlyingType = Nullable.GetUnderlyingType(typeof(TValue)); if (RuntimeTypeInfoCache.IsContainerType(underlyingType)) { // IMPORTANT: Do NOT add a token to the stream since we are triggering a re-entrance on the same object here. // Unpack Nullable<T> as T var underlyingValue = Convert.ChangeType(value, underlyingType); if (!PropertyContainer.TryAccept(this, ref underlyingValue, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } } else { BinarySerialization.WritePrimitiveBoxed(m_Stream, value, underlyingType); } return; } if (!RuntimeTypeInfoCache <TValue> .IsValueType) { #if !UNITY_DOTSPLAYER if (!(isRoot && m_DisableRootAdapters) && value is UnityEngine.Object unityEngineObject) { // Special path for polymorphic unity object references. m_Stream->Add(k_TokenUnityEngineObjectReference); (m_Adapters.Internal as Adapters.Contravariant.IBinaryAdapter <UnityEngine.Object>).Serialize(new BinarySerializationContext <TValue>(this, default, value, isRoot), unityEngineObject);
void WriteValue <TValue>(TValue value, bool isRoot = false) { var runAdapters = !(isRoot && m_DisableRootAdapters); if (runAdapters && m_Adapters.TrySerialize(m_Stream, ref value)) { return; } if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.WritePrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } if (RuntimeTypeInfoCache <TValue> .CanBeNull && null == value) { m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsNullable) { m_Stream->Add(k_TokenNone); BinarySerialization.WritePrimitiveBoxed(m_Stream, value, Nullable.GetUnderlyingType(typeof(TValue))); return; } if (!RuntimeTypeInfoCache <TValue> .IsValueType) { #if !UNITY_DOTSPLAYER if (runAdapters && value is UnityEngine.Object unityEngineObject) { // Special path for polymorphic unity object references. m_Stream->Add(k_TokenUnityEngineObjectReference); m_Adapters.TrySerialize(m_Stream, ref unityEngineObject); return; } #endif if (null != m_SerializedReferences && !value.GetType().IsValueType) { // At this point we don't know if an object will reference this value. // To avoid a second visitation pass we always create an entry for each managed object reference. var id = m_SerializedReferences.AddSerializedReference(value); if (!m_SerializedReferences.SetSerialized(value)) { // This is the second time encountering this object during serialization. // We instead of writing out the object we simply write the id. m_Stream->Add(k_TokenSerializedReference); m_Stream->Add(id); return; } } // This is a very common case. At serialize time we are serializing something that is polymorphic or an object // However at deserialize time the user known the System.Type, we can avoid writing out the fully qualified type name in this case. var isRootAndTypeWasGiven = isRoot && null != m_SerializedType; if (typeof(TValue) != value.GetType() && !isRootAndTypeWasGiven) { m_Stream->Add(k_TokenPolymorphic); m_Stream->Add(value.GetType().AssemblyQualifiedName); } else { m_Stream->Add(k_TokenNone); } } if (RuntimeTypeInfoCache <TValue> .IsObjectType && !RuntimeTypeInfoCache.IsContainerType(value.GetType())) { BinarySerialization.WritePrimitiveBoxed(m_Stream, value, value.GetType()); return; } if (!PropertyContainer.Visit(ref value, this, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } }
bool CompareEquality <TValue>(TValue lhs, TValue rhs) { if (!RuntimeTypeInfoCache <TValue> .IsContainerType) { return(EqualityComparer <TValue> .Default.Equals(lhs, rhs)); } if (RuntimeTypeInfoCache <TValue> .CanBeNull) { if (null == lhs) { return(null == rhs); } if (null == rhs) { return(false); } } if (!RuntimeTypeInfoCache <TValue> .IsValueType) { if (ReferenceEquals(lhs, rhs)) { return(true); } var type = lhs.GetType(); #if !UNITY_DOTSPLAYER // UnityEngine references can be copied as-is. if (typeof(UnityEngine.Object).IsAssignableFrom(type)) { return(EqualityComparer <TValue> .Default.Equals(lhs, rhs)); } #endif // Boxed value types can be compared as-is using the default comparer (with boxing). if (!RuntimeTypeInfoCache.IsContainerType(type)) { return(EqualityComparer <TValue> .Default.Equals(lhs, rhs)); } var isReferenceType = !(type.IsValueType || type == typeof(string)); if (isReferenceType) { // If this is a reference within the same object. We need to compare against the reference within the other object. // This is to support things like circular references/graph structures. // The root object is not added to the map so we need to explicitly check for it. if (m_LhsObject == (object)lhs) { return(m_RhsObject.Equals(rhs)); } // Otherwise let's check the map if it exists. if (null != m_References && m_References.TryGetValue(lhs, out var existingReference)) { return(existingReference.Equals(rhs)); } // Retain a mapping of references within this object. This is needed to support things like circular references. if (null == m_References) { m_References = new Dictionary <object, object>(); } m_References.Add(lhs, rhs); } } // We now have a properly constructed instance of the dstValue, nest in and copy all members. // Unity.Properties will automatically handle collection type from this point. var dstContainer = m_Stack; m_Stack = rhs; PropertyContainer.Visit(ref lhs, this, out _); m_Stack = dstContainer; return(m_Equals); }
void ReadValue <TValue>(ref TValue value, bool isRoot = false) { var runAdapters = !(isRoot && m_DisableRootAdapters); if (runAdapters && m_Adapters.TryDeserialize(m_Stream, ref value)) { return; } if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.ReadPrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } var token = default(byte); if (RuntimeTypeInfoCache <TValue> .CanBeNull) { token = m_Stream->ReadNext <byte>(); switch (token) { case k_TokenNull: value = default; return; case k_TokenSerializedReference: var id = m_Stream->ReadNext <int>(); if (null == m_SerializedReferences) { throw new Exception("Deserialization encountered a serialized object reference while running with DisableSerializedReferences."); } value = (TValue)m_SerializedReferences.GetDeserializedReference(id); return; } } if (RuntimeTypeInfoCache <TValue> .IsNullable) { BinarySerialization.ReadPrimitiveBoxed(m_Stream, ref value, Nullable.GetUnderlyingType(typeof(TValue))); return; } #if !UNITY_DOTSPLAYER if (runAdapters && token == k_TokenUnityEngineObjectReference) { var unityEngineObject = default(UnityEngine.Object); m_Adapters.TryDeserialize(m_Stream, ref unityEngineObject); value = (TValue)(object)unityEngineObject; return; } #endif if (token == k_TokenPolymorphic) { m_Stream->ReadNext(out var assemblyQualifiedTypeName); if (string.IsNullOrEmpty(assemblyQualifiedTypeName)) { throw new ArgumentException(); } var concreteType = Type.GetType(assemblyQualifiedTypeName); if (null == concreteType) { if (FormerNameAttribute.TryGetCurrentTypeName(assemblyQualifiedTypeName, out var currentAssemblyQualifiedTypeName)) { concreteType = Type.GetType(currentAssemblyQualifiedTypeName); } if (null == concreteType) { throw new ArgumentException(); } } m_SerializedTypeProviderSerializedType = concreteType; } else { // If we have a user provided root type pass it to the type construction. m_SerializedTypeProviderSerializedType = isRoot ? m_SerializedType : null; } DefaultTypeConstruction.Construct(ref value, this); if (RuntimeTypeInfoCache <TValue> .IsObjectType && !RuntimeTypeInfoCache.IsContainerType(value.GetType())) { BinarySerialization.ReadPrimitiveBoxed(m_Stream, ref value, value.GetType()); return; } if (!PropertyContainer.Visit(ref value, this, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } }
internal void ReadValueWithoutAdapters <TValue>(ref TValue value, bool isRoot) { if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.ReadPrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } var token = default(byte); if (RuntimeTypeInfoCache <TValue> .CanBeNull) { token = m_Stream->ReadNext <byte>(); switch (token) { case k_TokenNull: value = default; return; case k_TokenSerializedReference: var id = m_Stream->ReadNext <int>(); if (null == m_SerializedReferences) { throw new Exception("Deserialization encountered a serialized object reference while running with DisableSerializedReferences."); } value = (TValue)m_SerializedReferences.GetDeserializedReference(id); return; } } if (RuntimeTypeInfoCache <TValue> .IsNullable) { var underlyingType = Nullable.GetUnderlyingType(typeof(TValue)); if (RuntimeTypeInfoCache.IsContainerType(underlyingType)) { m_SerializedTypeProviderSerializedType = underlyingType; DefaultTypeConstruction.Construct(ref value, this); var underlyingValue = Convert.ChangeType(value, underlyingType); if (!PropertyContainer.TryAccept(this, ref underlyingValue, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } // Repack the T as Nullable<T> value = (TValue)underlyingValue; } else { BinarySerialization.ReadPrimitiveBoxed(m_Stream, ref value, Nullable.GetUnderlyingType(typeof(TValue))); } return; } #if !UNITY_DOTSPLAYER if (!(isRoot && m_DisableRootAdapters) && token == k_TokenUnityEngineObjectReference) { value = (TValue)(m_Adapters.Internal as Adapters.Contravariant.IBinaryAdapter <UnityEngine.Object>).Deserialize(new BinaryDeserializationContext <TValue>(this, default, isRoot));;