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;