From a06a764f74e349d7b909e74d0a3d2e965f50d165 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 16:57:36 +0000 Subject: [PATCH 1/8] Bump System.Collections.Immutable from 1.6.0 to 1.7.0 (#148) --- src/Hyperion.Tests/Hyperion.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hyperion.Tests/Hyperion.Tests.csproj b/src/Hyperion.Tests/Hyperion.Tests.csproj index 8bebdc43..782667db 100644 --- a/src/Hyperion.Tests/Hyperion.Tests.csproj +++ b/src/Hyperion.Tests/Hyperion.Tests.csproj @@ -11,7 +11,7 @@ - + From d0f963a11114a36e87e303e685871ae2bf6f8d84 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 17:52:32 +0000 Subject: [PATCH 2/8] Bump System.Reflection.TypeExtensions from 4.6.0 to 4.7.0 (#147) --- src/Hyperion/Hyperion.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hyperion/Hyperion.csproj b/src/Hyperion/Hyperion.csproj index ea82a236..99ebc54c 100644 --- a/src/Hyperion/Hyperion.csproj +++ b/src/Hyperion/Hyperion.csproj @@ -10,7 +10,7 @@ - + From 54e13f06491efbeffd7560e346dda507a0d99677 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2020 21:14:36 +0000 Subject: [PATCH 3/8] Bump Microsoft.CSharp from 4.6.0 to 4.7.0 (#146) --- src/Hyperion/Hyperion.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Hyperion/Hyperion.csproj b/src/Hyperion/Hyperion.csproj index 99ebc54c..3da7b049 100644 --- a/src/Hyperion/Hyperion.csproj +++ b/src/Hyperion/Hyperion.csproj @@ -11,12 +11,12 @@ - + - + From 3d65b93b8d879b7619de6fc4b68c1aaee44ca3b3 Mon Sep 17 00:00:00 2001 From: Igor Fedchenko Date: Fri, 17 Jan 2020 02:47:16 +0300 Subject: [PATCH 4/8] Fix version tolerance when bad assembly version specified in type manifest (#149) * Added red test * Ignore assembly version, if specified * Handling assembly loading failure * Fix mscorelib substitution case * Fixed ToQualifiedAssemblyName test * Strip assembly version with regex * Reuse existing assembly version strip regex --- src/Hyperion.Tests/Bugs.cs | 68 +++++++++++++++++++ src/Hyperion.Tests/Hyperion.Tests.csproj | 1 + src/Hyperion/Extensions/TypeEx.cs | 28 +++++++- src/Hyperion/Hyperion.csproj | 5 ++ .../ValueSerializers/TypeSerializer.cs | 3 +- 5 files changed, 100 insertions(+), 5 deletions(-) diff --git a/src/Hyperion.Tests/Bugs.cs b/src/Hyperion.Tests/Bugs.cs index 1be64854..a4ebe36c 100644 --- a/src/Hyperion.Tests/Bugs.cs +++ b/src/Hyperion.Tests/Bugs.cs @@ -11,6 +11,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using FluentAssertions; +using Hyperion.Extensions; using Xunit; namespace Hyperion.Tests @@ -85,6 +90,69 @@ public void CanSerializeMessageWithByte() var res = serializer.Deserialize(stream); } + /// + /// Fix for https://github.com/akkadotnet/Hyperion/issues/144 + /// + [Fact] + public void CanFindTypeByManifest_WhenManifestContainsUnknownAssemblyVersion() + { + var serializer = new Serializer(new SerializerOptions(versionTolerance: true, preserveObjectReferences: true)); + var type = typeof(ByteMessage); + + MemoryStream GetStreamForManifest(string manifest) + { + var stream = new MemoryStream(); + stream.WriteLengthEncodedByteArray(manifest.ToUtf8Bytes(), serializer.GetSerializerSession()); + stream.Position = 0; + return stream; + } + + // This is used in serialized manifest, should be something like 'Hyperion.Tests.Bugs+ByteMessage, Hyperion.Tests' + var shortName = type.GetShortAssemblyQualifiedName(); + var shortNameStream = GetStreamForManifest(shortName); + // Something like 'Hyperion.Tests.Bugs+ByteMessage, Hyperion.Tests, Version=0.9.11.0, Culture=neutral, PublicKeyToken=null' + var fullName = type.AssemblyQualifiedName; + var fullNameStream = GetStreamForManifest(fullName); + // Set bad assembly version to make deserialization fail + var fullNameWithUnknownVersion = fullName.Remove(fullName.IndexOf(", Version=")) + ", Version=999999, Culture=neutral, PublicKeyToken=null"; + var fullNameWithUnknownVersionStream = GetStreamForManifest(fullNameWithUnknownVersion); + + this.Invoking(_ => TypeEx.GetTypeFromManifestFull(shortNameStream, serializer.GetDeserializerSession())) + .Should().NotThrow("When assembly short name is specified in manifest, should work"); + this.Invoking(_ => TypeEx.GetTypeFromManifestFull(fullNameStream, serializer.GetDeserializerSession())) + .Should().NotThrow("When assembly fully qualified name specified and name is correct, should work even before fix"); + // This one was initially failing + this.Invoking(_ => TypeEx.GetTypeFromManifestFull(fullNameWithUnknownVersionStream, serializer.GetDeserializerSession())) + .Should().NotThrow("When assembly fully qualified name specified and unknown/wrong, type should be detected anyway"); + } + + [Fact] + public void TypeEx_ToQualifiedAssemblyName_should_strip_version_correctly_for_mscorlib_substitution() + { + var version = TypeEx.ToQualifiedAssemblyName( + "System.Collections.Immutable.ImmutableDictionary`2[[System.String, mscorlib,%core%],[System.Int32, mscorlib,%core%]]," + + " System.Collections.Immutable, Version=1.2.1.0, PublicKeyToken=b03f5f7f11d50a3a", + ignoreAssemblyVersion: true); + + var coreAssemblyName = typeof(TypeEx).GetField("CoreAssemblyName", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); + if (coreAssemblyName == null) + throw new Exception($"CoreAssemblyName private static field does not exist in {nameof(TypeEx)} class anymore"); + + version.Should().Be("System.Collections.Immutable.ImmutableDictionary`2" + + $"[[System.String, mscorlib{coreAssemblyName}],[System.Int32, mscorlib{coreAssemblyName}]], System.Collections.Immutable"); + } + + [Fact] + public void TypeEx_ToQualifiedAssemblyName_should_strip_version_correctly_for_multiple_versions_specified() + { + var version = TypeEx.ToQualifiedAssemblyName( + "System.Collections.Immutable.ImmutableList`1[[Foo.Bar, Foo, Version=2019.12.10.1]], " + + "System.Collections.Immutable, Version=1.2.2.0, PublicKeyToken=b03f5f7f11d50a3a", + ignoreAssemblyVersion: true); + + version.Should().Be("System.Collections.Immutable.ImmutableList`1[[Foo.Bar, Foo]], System.Collections.Immutable"); + } + [Fact] public void CanSerialieCustomType_bug() { diff --git a/src/Hyperion.Tests/Hyperion.Tests.csproj b/src/Hyperion.Tests/Hyperion.Tests.csproj index 782667db..cce01f9f 100644 --- a/src/Hyperion.Tests/Hyperion.Tests.csproj +++ b/src/Hyperion.Tests/Hyperion.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Hyperion/Extensions/TypeEx.cs b/src/Hyperion/Extensions/TypeEx.cs index 32f31f1e..c917417f 100644 --- a/src/Hyperion/Extensions/TypeEx.cs +++ b/src/Hyperion/Extensions/TypeEx.cs @@ -130,11 +130,26 @@ private static Type GetTypeFromManifestName(Stream stream, DeserializerSession s shortName = shortName.Replace("mscorlib,%core%", "System.Private.CoreLib,%core%"); } #endif + return LoadTypeByName(shortName); + }); + } - var typename = ToQualifiedAssemblyName(shortName); + public static Type LoadTypeByName(string name) + { + try + { + // Try to load type name using strict version to avoid possible conflicts + // i.e. if there are different version available in GAC and locally + var typename = ToQualifiedAssemblyName(name, ignoreAssemblyVersion: false); return Type.GetType(typename, true); - }); + } + catch (FileLoadException) + { + var typename = ToQualifiedAssemblyName(name, ignoreAssemblyVersion: true); + return Type.GetType(typename, true); + } } + public static Type GetTypeFromManifestFull(Stream stream, DeserializerSession session) { var type = GetTypeFromManifestName(stream, session); @@ -228,9 +243,16 @@ public static string GetShortAssemblyQualifiedName(this Type self) return name; } - public static string ToQualifiedAssemblyName(string shortName) + public static string ToQualifiedAssemblyName(string shortName, bool ignoreAssemblyVersion) { + // Strip out assembly version, if specified + if (ignoreAssemblyVersion) + { + shortName = cleanAssemblyVersionRegex.Replace(shortName, string.Empty); + } + var res = shortName.Replace(",%core%", CoreAssemblyName); + return res; } diff --git a/src/Hyperion/Hyperion.csproj b/src/Hyperion/Hyperion.csproj index 3da7b049..e9c2a110 100644 --- a/src/Hyperion/Hyperion.csproj +++ b/src/Hyperion/Hyperion.csproj @@ -24,6 +24,11 @@ + + + <_Parameter1>Hyperion.Tests + + $(DefineConstants);NETSTANDARD16 diff --git a/src/Hyperion/ValueSerializers/TypeSerializer.cs b/src/Hyperion/ValueSerializers/TypeSerializer.cs index db4a3059..bee8d1b9 100644 --- a/src/Hyperion/ValueSerializers/TypeSerializer.cs +++ b/src/Hyperion/ValueSerializers/TypeSerializer.cs @@ -66,8 +66,7 @@ public override object ReadValue(Stream stream, DeserializerSession session) if (shortname == null) return null; - var name = TypeEx.ToQualifiedAssemblyName(shortname); - var type = Type.GetType(name,true); + var type = TypeEx.LoadTypeByName(shortname); //add the deserialized type to lookup if (session.Serializer.Options.PreserveObjectReferences) From 7f36823f43327833c1b5022c0d5f6408e081965b Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Mon, 20 Jan 2020 12:45:40 -0600 Subject: [PATCH 5/8] Adjust ArrayList support (#150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support ArrayList * Remove wrong copyright * Communicate between mscorlib and System.Private.CoreLib * Strict string match * Add a binary file generator for all .NET frameworks * Add generated serialized files to project as suggestted in https://github.com/akkadotnet/Hyperion/pull/112#issuecomment-486078678_ * Add unit tests to test cross framework serialization and deserialization * Readd new line * added comment to explain null-coalescing operation Co-authored-by: alexvaut Co-authored-by: humhei Co-authored-by: Deniz İrgin --- src/Hyperion.Tests/CollectionTests.cs | 24 +++++++++++++++++++ .../EnumerableSerializerFactory.cs | 11 +++++++++ 2 files changed, 35 insertions(+) diff --git a/src/Hyperion.Tests/CollectionTests.cs b/src/Hyperion.Tests/CollectionTests.cs index b316801a..caf0b0c6 100644 --- a/src/Hyperion.Tests/CollectionTests.cs +++ b/src/Hyperion.Tests/CollectionTests.cs @@ -186,6 +186,30 @@ public void CanSerializeList() Assert.Equal(expected, actual); } + [Fact] + public void CanSerializeArrayList() + { + var expected = new ArrayList() + { + new Something + { + BoolProp = true, + Else = new Else + { + Name = "Yoho" + }, + Int32Prop = 999, + StringProp = "Yesbox!" + }, + "a", 123 + }; + + Serialize(expected); + Reset(); + var actual = Deserialize(); + Assert.Equal(expected, actual); + } + [Fact] public void CanSerializeLinkedList() { diff --git a/src/Hyperion/SerializerFactories/EnumerableSerializerFactory.cs b/src/Hyperion/SerializerFactories/EnumerableSerializerFactory.cs index f76c6dcb..ebadbf2b 100644 --- a/src/Hyperion/SerializerFactories/EnumerableSerializerFactory.cs +++ b/src/Hyperion/SerializerFactories/EnumerableSerializerFactory.cs @@ -83,6 +83,17 @@ private static Type GetEnumerableType(Type type) private static ConstructorInfo GetEnumerableConstructor(Type type) { var enumerableType = GetEnumerableType(type); + + /* + * In the event that we're serializing a non-generic IEnumerable such as an ArrayList, + * there aren't any generic parameters and the type returned will just be System.Object. + * + * The GetEnumerableType method extracts the generic argument from the IEnumerable and uses that + * by default, however it will return null only for a non-generic implementation thus we use the + * null-coalescing operator to specify the return type as System.Object in order to serialize ArrayList + * types without errors. + */ + enumerableType = enumerableType ?? typeof(object); var iEnumerableType = typeof(IEnumerable<>).MakeGenericType(enumerableType); return enumerableType != null ? type.GetTypeInfo() From a13c315c838218623bfba5108339cec77926b266 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Mon, 20 Jan 2020 13:23:58 -0600 Subject: [PATCH 6/8] rewrote CrossFrameworkSerializationTests as xUnit theory (#152) --- .../CrossFrameworkSerializationTests.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Hyperion.Tests/CrossFrameworkSerializationTests.cs b/src/Hyperion.Tests/CrossFrameworkSerializationTests.cs index 08d5690f..69ba00fd 100644 --- a/src/Hyperion.Tests/CrossFrameworkSerializationTests.cs +++ b/src/Hyperion.Tests/CrossFrameworkSerializationTests.cs @@ -1,4 +1,7 @@ -using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; using Hyperion.Tests.Generator; using Xunit; @@ -15,22 +18,22 @@ public CrossFrameworkSerializationTests() _originalObject = CrossFrameworkInitializer.Init(); } - [Fact] - public void CanSerializeCrossFramework() + public static IEnumerable SerializationFiles() { const string defaultOutputPath = CrossFrameworkInitializer.DefaultOutputPath; var testFiles = Directory.GetFiles(defaultOutputPath, "*.tf"); + return testFiles.Select(x => new object[] { x }); + } - Assert.NotEmpty(testFiles); - - foreach (string testFile in testFiles) + [Theory] + [MemberData(nameof(SerializationFiles))] + public void CanSerializeCrossFramework(string fileName) + { + using (var fileStream = new FileStream(fileName, FileMode.Open)) { - using (var fileStream = new FileStream(testFile, FileMode.Open)) - { - var crossFrameworkClass = _serializer.Deserialize(fileStream); + var crossFrameworkClass = _serializer.Deserialize(fileStream); - Assert.Equal(_originalObject, crossFrameworkClass); - } + Assert.Equal(_originalObject, crossFrameworkClass); } } } From 149fe9fd25b726e768ad615502164bd727c96b47 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Mon, 20 Jan 2020 14:15:05 -0600 Subject: [PATCH 7/8] added v0.9.2 release notes (#153) --- RELEASE_NOTES.md | 5 +++-- src/common.props | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6abf2a45..1bb23226 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,4 @@ -### 0.9.11 November 13 2019 #### +### 0.9.12 January 20 2020 #### -[Hyperion now targets .NET 4.5 again so it can be included inside Akka.NET v1.3.* releases](https://github.com/akkadotnet/Hyperion/pull/141). \ No newline at end of file +* Added version tolerance during deserialization. +* Added `ArrayList` and non-generic `IEnumerable` support. \ No newline at end of file diff --git a/src/common.props b/src/common.props index 6797e68e..10118f1e 100644 --- a/src/common.props +++ b/src/common.props @@ -2,8 +2,9 @@ Copyright © 2016-2017 Akka.NET Team Akka.NET Team - 0.9.11 - [Hyperion now targets .NET 4.5 again so it can be included inside Akka.NET v1.3.* releases](https://github.com/akkadotnet/Hyperion/pull/141). + 0.9.12 + Added version tolerance during deserialization. +Added `ArrayList` and non-generic `IEnumerable` support. http://getakka.net/images/akkalogo.png https://github.com/akkadotnet/Hyperion https://github.com/akkadotnet/Hyperion/blob/master/LICENSE From f09ffdd718bfb813a98fc92352f0fa58a1353d63 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Mon, 20 Jan 2020 14:15:51 -0600 Subject: [PATCH 8/8] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c092452..c8762931 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![Join the chat at https://gitter.im/akkadotnet/Hyperion](https://badges.gitter.im/akkadotnet/Hyperion.svg)](https://gitter.im/akkadotnet/Hyperion?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -A high performance polymorphic serializer for the .NET framework, fork of the [Wire](https://github.com/rogeralsing/Wire) serializer. +A high performance polymorphic serializer for the .NET framework. -Current status: **BETA** (v0.9.7). +Current status: **BETA** (v0.9.12). ## License Licensed under Apache 2.0, see [LICENSE](LICENSE) for the full text.