This is an automated email from the ASF dual-hosted git repository.

ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new f6e2ebe8e62 IGNITE-17299 .NET: Fix StringComparer serialization 
(#10214)
f6e2ebe8e62 is described below

commit f6e2ebe8e62b3e442bda2a70f19544261b3c0cfa
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Thu Aug 25 06:56:18 2022 +0300

    IGNITE-17299 .NET: Fix StringComparer serialization (#10214)
    
    * Handle special case when deserialization constructor is missing.
    * Add tests for all documented serializable system types.
---
 .../Serializable/BasicSerializableObjectsTest.cs   | 316 +++++++++++++++++++++
 .../Services/ServicesTest.cs                       |   1 +
 .../Impl/Binary/SerializableSerializer.cs          |  34 ++-
 .../Impl/Common/SerializableTypeDescriptor.cs      |   8 +-
 .../dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs  |   1 -
 5 files changed, 342 insertions(+), 18 deletions(-)

diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/Serializable/BasicSerializableObjectsTest.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/Serializable/BasicSerializableObjectsTest.cs
index 24c69ddba08..021e22ada8d 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/Serializable/BasicSerializableObjectsTest.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/Serializable/BasicSerializableObjectsTest.cs
@@ -18,7 +18,16 @@
 namespace Apache.Ignite.Core.Tests.Binary.Serializable
 {
     using System;
+    using System.Collections;
+    using System.Collections.Generic;
+    using System.Collections.ObjectModel;
+    using System.ComponentModel;
+    using System.Configuration;
+    using System.Globalization;
+    using System.IO;
+    using System.Linq;
     using System.Runtime.Serialization;
+    using Microsoft.CSharp.RuntimeBinder;
     using NUnit.Framework;
 
     /// <summary>
@@ -26,6 +35,288 @@ namespace Apache.Ignite.Core.Tests.Binary.Serializable
     /// </summary>
     public class BasicSerializableObjectsTest
     {
+#pragma warning disable CS0618
+        /// <summary>
+        /// Source:
+        /// 
https://docs.microsoft.com/en-us/dotnet/standard/serialization/binary-serialization#serializable-types
+        /// </summary>
+        private static readonly IReadOnlyList<object> SerializableTypeObjects 
= new[]
+        {
+            new RuntimeBinderException(),
+            new RuntimeBinderInternalCompilerException(),
+            new AccessViolationException(),
+            new AggregateException(),
+            new AppDomainUnloadedException(),
+            new ApplicationException(),
+            new ArgumentException(),
+            new ArgumentNullException(),
+            new ArgumentOutOfRangeException(),
+            new ArithmeticException(),
+            new ArraySegment<object>(),
+            new ArrayTypeMismatchException(),
+            new SerializableAttribute(),
+            new BadImageFormatException(),
+            new Boolean(),
+            new Byte(),
+            new CannotUnloadAppDomainException(),
+            new Char(),
+            new ArrayList(),
+            new BitArray(10),
+            new Comparer(CultureInfo.InvariantCulture),
+            new DictionaryEntry(),
+            StringComparer.CurrentCultureIgnoreCase,
+            new HashSet<int>(),
+            new KeyNotFoundException(),
+            new KeyValuePair<int, int>(),
+            new LinkedList<int>(),
+            new List<float>(),
+            new Queue<long>(),
+            new Dictionary<int, string>(),
+            new SortedDictionary<string, string>(),
+            new SortedList<float, double>(),
+            new SortedSet<int>(),
+            new Stack<int>(),
+            new Hashtable(),
+            new Collection<int>(),
+#if NETCOREAPP
+            new 
Newtonsoft.Json.Serialization.JsonPropertyCollection(typeof(object)),
+#endif
+            new ObservableCollection<string>(),
+            new ReadOnlyCollection<int>(Enumerable.Range(1, 10).ToList()),
+            new ReadOnlyDictionary<int, string>(Enumerable.Range(1, 
5).ToDictionary(x => x, x => x.ToString())),
+            new ReadOnlyObservableCollection<string>(new 
ObservableCollection<string>()),
+            new Queue(),
+            new SortedList(),
+            new System.Collections.Specialized.HybridDictionary(),
+            new System.Collections.Specialized.ListDictionary(),
+            new System.Collections.Specialized.OrderedDictionary(),
+            new System.Collections.Specialized.StringCollection(),
+            new System.Collections.Specialized.StringDictionary(),
+            new Stack(),
+            new BindingList<int>(),
+#if NETCOREAPP
+            new System.ComponentModel.DataAnnotations.ValidationException(),
+#endif
+            new System.ComponentModel.Design.CheckoutException(),
+            new InvalidAsynchronousStateException(),
+            new InvalidEnumArgumentException(),
+            new LicenseException(typeof(object)),
+            new WarningException(),
+            new Win32Exception(),
+            new ConfigurationErrorsException(),
+            new ConfigurationException("x"),
+            new System.Configuration.Provider.ProviderException(),
+            new SettingsPropertyIsReadOnlyException(),
+            new SettingsPropertyNotFoundException(),
+            new SettingsPropertyWrongTypeException(),
+            new ContextMarshalException(),
+            DBNull.Value,
+            new System.Data.ConstraintException(),
+            new System.Data.DBConcurrencyException(),
+            new System.Data.DataException(),
+            new System.Data.DataSet(),
+            new System.Data.DataTable(),
+            new System.Data.DeletedRowInaccessibleException(),
+            new System.Data.DuplicateNameException(),
+            new System.Data.EvaluateException(),
+            new System.Data.InRowChangingEventException(),
+            new System.Data.InvalidConstraintException(),
+            new System.Data.InvalidExpressionException(),
+            new System.Data.MissingPrimaryKeyException(),
+            new System.Data.NoNullAllowedException(),
+            new System.Data.PropertyCollection(),
+            new System.Data.ReadOnlyException(),
+            new System.Data.RowNotInTableException(),
+            new System.Data.SqlTypes.SqlAlreadyFilledException(),
+            new System.Data.SqlTypes.SqlBoolean(),
+            new System.Data.SqlTypes.SqlByte(),
+            new System.Data.SqlTypes.SqlDateTime(),
+            new System.Data.SqlTypes.SqlDouble(),
+            new System.Data.SqlTypes.SqlGuid(),
+            new System.Data.SqlTypes.SqlInt16(),
+            new System.Data.SqlTypes.SqlInt32(),
+            new System.Data.SqlTypes.SqlInt64(),
+            new System.Data.SqlTypes.SqlNotFilledException(),
+            new System.Data.SqlTypes.SqlNullValueException(),
+            new System.Data.SqlTypes.SqlString(),
+            new System.Data.SqlTypes.SqlTruncateException(),
+            new System.Data.SqlTypes.SqlTypeException(),
+            new System.Data.StrongTypingException(),
+            new System.Data.SyntaxErrorException(),
+            new System.Data.VersionNotFoundException(),
+            new DataMisalignedException(),
+            new DateTime(),
+            new DateTimeOffset(),
+            new Decimal(),
+            new System.Diagnostics.Tracing.EventSourceException(),
+            new DirectoryNotFoundException(),
+            new DivideByZeroException(),
+            new DllNotFoundException(),
+            new Double(),
+            new System.Drawing.Color(),
+            new System.Drawing.Point(),
+            new System.Drawing.PointF(),
+            new System.Drawing.Rectangle(),
+            new System.Drawing.RectangleF(),
+            new System.Drawing.Size(),
+            new System.Drawing.SizeF(),
+            new DuplicateWaitObjectException(),
+            new EntryPointNotFoundException(),
+            EventArgs.Empty,
+            new Exception(),
+            new ExecutionEngineException(),
+            new FieldAccessException(),
+            new FormatException(),
+            new CultureNotFoundException(),
+            new SortVersion(1, Guid.NewGuid()),
+            new Guid(),
+            new DriveNotFoundException(),
+            new EndOfStreamException(),
+            new FileLoadException(),
+            new FileNotFoundException(),
+            new IOException(),
+            new InternalBufferOverflowException(),
+            new InvalidDataException(),
+            new System.IO.IsolatedStorage.IsolatedStorageException(),
+            new PathTooLongException(),
+            new IndexOutOfRangeException(),
+            new InsufficientExecutionStackException(),
+            new InsufficientMemoryException(),
+            new Int16(),
+            new Int32(),
+            new Int64(),
+            new IntPtr(),
+            new InvalidCastException(),
+            new InvalidOperationException(),
+            new InvalidProgramException(),
+            new InvalidTimeZoneException(),
+            new MemberAccessException(),
+            new MethodAccessException(),
+            new MissingFieldException(),
+            new MissingMemberException(),
+            new MissingMethodException(),
+            new MulticastNotSupportedException(),
+            new System.Net.Cookie(),
+            new System.Net.CookieCollection(),
+            new System.Net.CookieContainer(),
+            new System.Net.CookieException(),
+            new System.Net.HttpListenerException(),
+            new System.Net.Mail.SmtpException(),
+            new System.Net.Mail.SmtpFailedRecipientException(),
+            new System.Net.Mail.SmtpFailedRecipientsException(),
+            new System.Net.NetworkInformation.NetworkInformationException(),
+            new System.Net.NetworkInformation.PingException("x"),
+            new System.Net.ProtocolViolationException(),
+            new System.Net.Sockets.SocketException(),
+            new System.Net.WebException(),
+            new System.Net.WebSockets.WebSocketException(),
+#if NETCOREAPP
+            new NotFiniteNumberException(),
+#endif
+            new NotImplementedException(),
+            new NotSupportedException(),
+            new NullReferenceException(),
+#if NETCOREAPP
+            new System.Numerics.BigInteger(),
+            new System.Numerics.Complex(),
+#endif
+            new Object(),
+            new ObjectDisposedException("x"),
+            new OperationCanceledException(),
+            new OutOfMemoryException(),
+            new OverflowException(),
+            new PlatformNotSupportedException(),
+            new RankException(),
+            new System.Reflection.AmbiguousMatchException(),
+            new System.Reflection.CustomAttributeFormatException(),
+            new System.Reflection.InvalidFilterCriteriaException(),
+            new System.Reflection.ReflectionTypeLoadException(null, null),
+            new System.Reflection.TargetException(),
+            new System.Reflection.TargetInvocationException("x", null),
+            new System.Reflection.TargetParameterCountException(),
+            new System.Resources.MissingManifestResourceException(),
+            new System.Resources.MissingSatelliteAssemblyException(),
+#if NETCOREAPP
+            new System.Runtime.CompilerServices.RuntimeWrappedException(new 
object()),
+#endif
+            new System.Runtime.InteropServices.COMException(),
+            new System.Runtime.InteropServices.ExternalException(),
+            new System.Runtime.InteropServices.InvalidComObjectException(),
+            new 
System.Runtime.InteropServices.InvalidOleVariantTypeException(),
+            new System.Runtime.InteropServices.MarshalDirectiveException(),
+            new System.Runtime.InteropServices.SEHException(),
+            new 
System.Runtime.InteropServices.SafeArrayRankMismatchException(),
+            new 
System.Runtime.InteropServices.SafeArrayTypeMismatchException(),
+            new InvalidDataContractException(),
+            new SerializationException(),
+            new SByte(),
+
+#if !NETCOREAPP
+            new System.Security.AccessControl.PrivilegeNotHeldException(),
+            new System.Security.Authentication.AuthenticationException(),
+            new System.Security.Authentication.InvalidCredentialException(),
+            new System.Security.Cryptography.CryptographicException(),
+            new 
System.Security.Cryptography.CryptographicUnexpectedOperationException(),
+            new System.Security.HostProtectionException(),
+            new System.Security.Policy.PolicyException(),
+            // Bug in .NET FW - missing deserialization constructor.
+            // new System.Security.Principal.IdentityNotMappedException(),
+            new System.Security.SecurityException(),
+            new System.Security.VerificationException(),
+            new System.Security.XmlSyntaxException(),
+#endif
+
+            new Single(),
+            new StackOverflowException(),
+            new SystemException(),
+            new System.Text.DecoderFallbackException(),
+            new System.Text.EncoderFallbackException(),
+            new System.Text.RegularExpressions.RegexMatchTimeoutException(),
+            new System.Text.StringBuilder(),
+            new System.Threading.AbandonedMutexException(),
+            new System.Threading.BarrierPostPhaseException(),
+            new System.Threading.LockRecursionException(),
+            new System.Threading.SemaphoreFullException(),
+            new System.Threading.SynchronizationLockException(),
+            new System.Threading.Tasks.TaskCanceledException(),
+            new System.Threading.Tasks.TaskSchedulerException(),
+            new System.Threading.ThreadInterruptedException(),
+            new System.Threading.ThreadStateException(),
+            new System.Threading.WaitHandleCannotBeOpenedException(),
+            new TimeSpan(),
+            TimeZoneInfo.Utc,
+            new TimeZoneNotFoundException(),
+            new TimeoutException(),
+            new System.Transactions.TransactionAbortedException(),
+            new System.Transactions.TransactionException(),
+            new System.Transactions.TransactionInDoubtException(),
+            new System.Transactions.TransactionManagerCommunicationException(),
+            new System.Transactions.TransactionPromotionException(),
+            new TypeAccessException(),
+            new TypeInitializationException("x", new Exception()),
+            new TypeLoadException(),
+            new TypeUnloadedException(),
+            new UInt16(),
+            new UInt32(),
+            new UInt64(),
+            new UIntPtr(),
+            new UnauthorizedAccessException(),
+            new Uri("https://example.com";),
+            new UriFormatException(),
+            new ValueTuple(),
+            new Version(),
+            new WeakReference<object>(new object()),
+            new WeakReference(new object()),
+            new System.Xml.Schema.XmlSchemaException(),
+            new System.Xml.Schema.XmlSchemaInferenceException(),
+            new System.Xml.Schema.XmlSchemaValidationException(),
+            new System.Xml.XPath.XPathException(),
+            new System.Xml.XmlException(),
+            new System.Xml.Xsl.XsltCompileException(),
+            new System.Xml.Xsl.XsltException()
+        };
+#pragma warning restore CS0618
+
         /// <summary>
         /// Tests the object with no fields.
         /// </summary>
@@ -80,6 +371,31 @@ namespace Apache.Ignite.Core.Tests.Binary.Serializable
             Assert.AreEqual(type.AssemblyQualifiedName, 
res.AssemblyQualifiedName);
         }
 
+        /// <summary>
+        /// Tests a special case with StringComparer.
+        /// </summary>
+        [Test]
+        public void TestComparer()
+        {
+            var obj = StringComparer.OrdinalIgnoreCase;
+            var res = TestUtils.SerializeDeserialize(obj);
+
+            Assert.AreEqual("OrdinalComparer", res.GetType().Name);
+            Assert.AreEqual(0, res.Compare("A", "a"), "Ignore case flag is 
deserialized incorrectly.");
+        }
+
+        /// <summary>
+        /// Tests all serializable system types.
+        /// </summary>
+        [Test, TestCaseSource(nameof(SerializableTypeObjects))]
+        public void TestAllSerializableSystemTypes(object obj)
+        {
+            Assert.IsNotNull(obj);
+            var res = TestUtils.SerializeDeserialize(obj);
+
+            Assert.IsNotNull(res);
+        }
+
         /// <summary>
         /// Missing serialization ctor.
         /// </summary>
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
index 4e763ab5906..2fbb7a4f2b0 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/ServicesTest.cs
@@ -1479,6 +1479,7 @@ namespace Apache.Ignite.Core.Tests.Services
         /// Creates a test caller context.
         /// </summary>
         /// <returns>Caller context.</returns>
+        // ReSharper disable once InconsistentNaming
         private IServiceCallContext callContext()
         {
             return new ServiceCallContextBuilder().Set("attr", 
"value").Build();
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs
index 2d87a1f86f9..b21c60039ce 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs
@@ -21,6 +21,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     using System.Collections.Generic;
     using System.IO;
     using System.Linq;
+    using System.Reflection;
     using System.Runtime.Serialization;
     using Apache.Ignite.Core.Binary;
     using Apache.Ignite.Core.Impl.Binary.Metadata;
@@ -330,15 +331,8 @@ namespace Apache.Ignite.Core.Impl.Binary
                 return new TypeResolver().ResolveType(serInfo.FullTypeName, 
serInfo.AssemblyName);
             }
 
-            if (serInfo.ObjectType != serializable.GetType() &&
-                typeof(ISerializable).IsAssignableFrom(serInfo.ObjectType))
+            if (serInfo.ObjectType != serializable.GetType())
             {
-                // serInfo.ObjectType should be ISerializable. There is a 
known case for generic collections:
-                // serializable is EnumEqualityComparer : ISerializable 
-                // and serInfo.ObjectType is ObjectEqualityComparer (does not 
implement ISerializable interface).
-                // Please read a possible explanation here:
-                // 
http://dotnetstudio.blogspot.ru/2012/06/net-35-to-net-40-enum.html
-
                 return serInfo.ObjectType;
             }
 
@@ -603,13 +597,33 @@ namespace Apache.Ignite.Core.Impl.Binary
         {
             var ctorFunc = 
SerializableTypeDescriptor.Get(customType).SerializationCtor;
 
-            var customObj = ctorFunc(serInfo, ctx);
+            var customObj = ctorFunc != null
+                ? ctorFunc(serInfo, ctx)
+                : FormatterServices.GetUninitializedObject(customType);
 
             var wrapper = customObj as IObjectReference;
 
-            return wrapper == null
+            var resObj = wrapper == null
                 ? customObj
                 : wrapper.GetRealObject(ctx);
+
+            // Special case: type is replaced, but there is no serialization 
ctor.
+            // Example: StringComparer.OrdinalIgnoreCase.
+            if (ctorFunc == null)
+            {
+                // Cached internally.
+                var members = 
FormatterServices.GetSerializableMembers(resObj.GetType());
+
+                foreach (var memberInfo in members)
+                {
+                    // FormatterServices.InternalGetSerializableMembers 
actually returns FieldInfo[],
+                    // so this cast is safe.
+                    var fieldInfo = (FieldInfo)memberInfo;
+                    fieldInfo.SetValue(resObj, 
serInfo.GetValue(fieldInfo.Name, fieldInfo.FieldType));
+                }
+            }
+
+            return resObj;
         }
 
         /// <summary>
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/SerializableTypeDescriptor.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/SerializableTypeDescriptor.cs
index f16e32a57f2..0ed35bec4fc 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/SerializableTypeDescriptor.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Common/SerializableTypeDescriptor.cs
@@ -119,13 +119,7 @@ namespace Apache.Ignite.Core.Impl.Common
         /// </summary>
         public Func<SerializationInfo, StreamingContext, object> 
SerializationCtor
         {
-            get
-            {
-                if (_serializationCtor == null)
-                    throw GetMissingCtorException();
-
-                return _serializationCtor;
-            }
+            get { return _serializationCtor; }
         }
 
         /// <summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs
index 1e0be1f286c..15a35a39815 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/IgniteUtils.cs
@@ -23,7 +23,6 @@ namespace Apache.Ignite.Core.Impl
     using System.Runtime.InteropServices;
     using System.Text;
     using Apache.Ignite.Core.Cache;
-    using Apache.Ignite.Core.Cache.Configuration;
     using Apache.Ignite.Core.Cluster;
     using Apache.Ignite.Core.Common;
     using Apache.Ignite.Core.Impl.Binary;

Reply via email to