-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Cross framework serialization finish up #20870
Cross framework serialization finish up #20870
Conversation
735a398
to
bb170d7
Compare
Note that the nested |
Thanks Justin. I already know that. We will discuss how to handle |
a532ba1
to
a0fc21a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@lmolkova Do you need Activity to be interchangeable between Core and Desktop or is it enough to be serializable from Core to Core? |
I don't know how much context I miss. I assume your question is related to cross AppDomain/process calls and presence of the Serializable attribute int the Activity. Note that Activity is serializable on NET 4.5 only. The whole Activity is not intended to be passed outside of the process, only its certain properties should be propagated along with the request. E.g. if an application makes HTTP call, some properties of the current Activity are passed in HTTP headers. So some protocol should decide how Activity properties are serialized into a particular transport. So Activity should not be interchangeable at all. May be @vancem could also comment on that if I missed anything. |
EDIT: the list was wrong :( |
What is it for? |
@lmolkova Thanks for the clarification. That means we don't need to put a TypeForward attribute on it and test roundtripping it from netfx to core and back. |
@jkotas to make sure that types are correctly serializing and deserializing from/to different platforms. Example given DataSet and DataTable don't override object Equals nor implement IEquatable. Therefore we need to manually check the cloned and original objects serializable fields for equality. Some of these types are covered by xunit Assert.Equal (I think it also tests items in collections which implement IEnumerable/IList). But some of them are definitely not covered like specialized collections. |
@ViktorHofer should we implement those in the product code? |
Why is it a problem? When you serialize/deserialize object on full .NET Framework, Equals for master and clone returns true for some types and returns false for other types. That's by design. I do not think we need to be fixing it. |
@jkotas - I believe this was meant for tests only so that we are able to asses if the type is cross-round-tripping (netfx->corefx) correctly |
I think I understand it now - it is just about making the test coverage better, not about fixing the product. |
If you want to go through API reviews for each type, then go for it. As this probably would take a lot of time and arguing and we want to test this till next week, I would not suggest it ;) |
Correct. |
70556d9
to
5cd3774
Compare
bbc4e28
to
5b428ad
Compare
@@ -810,22 +815,24 @@ private void BuildCookieCollectionFromDomainMatches(Uri uri, bool isSecure, int | |||
PathList pathList; | |||
lock (m_domainTable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, this should be lock (m_domainTable.SyncRoot)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DEfinitely should fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue on line 866 below the comment // (This is the only place that does domain removal)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -810,22 +815,24 @@ private void BuildCookieCollectionFromDomainMatches(Uri uri, bool isSecure, int | |||
PathList pathList; | |||
lock (m_domainTable) | |||
{ | |||
if (!m_domainTable.TryGetValue(domainAttribute[i], out pathList)) | |||
object pathListValue = m_domainTable[domainAttribute[i]]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need for pathListValue
you can just assign directly to pathList
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like desktop does that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not changing
@@ -950,7 +958,7 @@ internal string GetCookieHeader(Uri uri, out string optCookie2) | |||
|
|||
string delimiter = string.Empty; | |||
|
|||
var builder = StringBuilderCache.Acquire(); | |||
StringBuilder builder = StringBuilderCache.Acquire(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI for ask-mode changes, we try to avoid unnecessary unrelated changes like this. Reasons are (1) distracting from actual diff (2) in general, adds risk. Not worth changing this now, especially because of the volume of other changes...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
count += cc.Count; | ||
} | ||
} | ||
return count; | ||
} | ||
|
||
public CookieCollection this[string s] | ||
public ICollection Values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to add this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I see desktop had it (and with no explicit lock) so I guess it is best to add it as you did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
|
||
public static PathList Create() => new PathList(new SortedList<string, CookieCollection>(PathListComparer.StaticInstance)); | ||
public static PathList Create() => new PathList(SortedList.Synchronized(new SortedList(PathListComparer.StaticInstance))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why this was .Synchronized
before since evidently when we changed to the generic form we concluded that the locks below were enough. Especially since we exposed no way to write to it.
But, it is safest to match desktop here as you have done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure whether we decided to make synchronized SortedLists serializable. I'd suggest checking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know. That's why I changed it: https://github.com/dotnet/corefx/pull/20870/files#diff-f48fff7b7909f489c5e8b8599c22843cR614
@@ -48,15 +49,16 @@ public sealed class Cookie | |||
private CookieVariant m_cookieVariant = CookieVariant.Plain; // Do not rename (binary serialization) | |||
private bool m_discard = false; // Do not rename (binary serialization) | |||
private string m_domain = string.Empty; // Do not rename (binary serialization) | |||
private bool m_domainImplicit = true; // Do not rename (binary serialization) | |||
private bool m_domain_implicit = true; // Do not rename (binary serialization) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these variables being renamed to use underscores?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Must match desktop to serialize between.
|
||
for (int i = 0; i < ports.Length; ++i) | ||
{ | ||
// Skip spaces | ||
if (ports[i] != string.Empty) | ||
{ | ||
if (!Int32.TryParse(ports[i], out port)) | ||
if (!Int32.TryParse(ports[i], out int port)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the purpose of this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like cleanup, which we can revert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reverted it
@@ -11,7 +11,7 @@ namespace System.Net | |||
// | |||
// A list of cookies maintained in Sorted order. Only one cookie with matching Name/Domain/Path | |||
[Serializable] | |||
[System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")] | |||
[System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we adding "Culture=neutral" attributes? Is this different from .NET Framework?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we need it for BinaryFormatter blob optimization. If you want to know it in more detail just ping me
@@ -100,8 +100,7 @@ public class CookieContainer | |||
new HeaderVariantInfo(HttpKnownHeaderNames.SetCookie2, CookieVariant.Rfc2965) | |||
}; | |||
|
|||
// NOTE: all accesses of _domainTable must be performed with _domainTable locked. | |||
private readonly Dictionary<string, PathList> m_domainTable = new Dictionary<string, PathList>(); // Do not rename (binary serialization) | |||
private readonly Hashtable m_domainTable = new Hashtable(); // Do not rename (binary serialization) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why change to Hashtable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because Desktop uses Hashtable and we are using vanilla binary serialization so the types and names must match.
Ah yes I didn't see that. Fine. |
{ | ||
foreach (KeyValuePair<string, PathList> entry in m_domainTable) | ||
foreach (DictionaryEntry entry in m_domainTable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to allocate a boxed object for each entry in the dictionary (see #7539, #9050, #10247). It'd be better to do something like:
// Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
IDictionaryEnumerator e = m_domainTable.GetEnumerator();
while (e.MoveNext())
{
if (domain == null)
{
tempDomain = (string)e.Key;
pathList = (PathList)e.Value;
}
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can change that later. Or even better, if you want to help you could spin up a PR which optimizes my recent changes without killing the just introduced serialization support.
} | ||
|
||
lock (pathList.SyncRoot) | ||
{ | ||
foreach (KeyValuePair<string, CookieCollection> pair in pathList) | ||
foreach (DictionaryEntry entry in pathList) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to allocate a boxed object for each entry in the dictionary. I wonder if PathList.GetEnumerator
should be changed to return IDictionaryEnumerator
instead of IEnumerator
, so the allocations can be avoided while iterating through pathList.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can change that later. Or even better, if you want to help you could spin up a PR which optimizes my recent changes without killing the just introduced serialization support.
@@ -1061,14 +1072,15 @@ public object SyncRoot | |||
} | |||
|
|||
[Serializable] | |||
private sealed class PathListComparer : IComparer<string> | |||
[System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] | |||
sealed class PathListComparer : IComparer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought this was needed for cross framework serialization. I might be wrong
@@ -983,15 +991,16 @@ public void SetCookies(Uri uri, string cookieHeader) | |||
} | |||
|
|||
[Serializable] | |||
[System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] | |||
internal struct PathList |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that this is going back to being stored in Hashtable
(and therefore is going to be boxed anyway), should it be changed back to a class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can change that later. Or even better, if you want to help you could spin up a PR which optimizes my recent changes without killing the just introduced serialization support.
@@ -737,8 +742,8 @@ internal CookieCollection InternalGetCookies(Uri uri) | |||
int port = uri.Port; | |||
CookieCollection cookies = null; | |||
|
|||
List<string> domainAttributeMatchAnyCookieVariant = new List<string>(); | |||
List<string> domainAttributeMatchOnlyCookieVariantPlain = null; | |||
var domainAttributeMatchAnyCookieVariant = new Collections.Generic.List<string>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a partial namespace here is a bit strange looking. Why not just leave it as List? If we need namespace qualification, please use the full namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler couldn't distinguish between SortedList generic and non-generic. I will add the fully namespace here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
public static PathList Create() => new PathList(new SortedList<string, CookieCollection>(PathListComparer.StaticInstance)); | ||
public static PathList Create() => new PathList(SortedList.Synchronized(new SortedList(PathListComparer.StaticInstance))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure whether we decided to make synchronized SortedLists serializable. I'd suggest checking.
} | ||
catch (Exception) | ||
{ | ||
Console.WriteLine("Error during equality check of type " + obj?.GetType()?.FullName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't you just add a message to the assert?
BinaryFormatterTests.CheckEquals(@this.Comparer, other.Comparer))) | ||
return false; | ||
|
||
return BinaryFormatterTests.CheckSequenceEquals(@this, other); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure ordering is guaranteed in different hash sets
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@krwq please review this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@morganbr @ViktorHofer currently no failures related to that so seems like order is preserved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@krwq , I expect that ordering may change in the future, such as when #14354 is completed. It would be good if this test wasn't sensitive to that.
@this.IsReadOnly == other.IsReadOnly && | ||
@this.IsFixedSize == other.IsFixedSize && | ||
@this.IsSynchronized == other.IsSynchronized && | ||
BinaryFormatterTests.CheckSequenceEquals(@this.Keys, other.Keys) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same set ordering issue
other != null && | ||
BinaryFormatterTests.CheckEquals(@this.Comparer, other.Comparer) && | ||
@this.Count == other.Count && | ||
BinaryFormatterTests.CheckSequenceEquals(@this.Keys, other.Keys) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same set ordering issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@krwq please review this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@morganbr @ViktorHofer because we don't have any failures related to that I'd leave as is - I wouldn't fix more than needed in this PR
{ | ||
if (!(@this != null && | ||
other != null && | ||
BinaryFormatterTests.CheckSequenceEquals(@this.Keys, other.Keys) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same set ordering issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@krwq please review this comment
if (!(@this != null && | ||
other != null && | ||
@this.Count == other.Count && | ||
BinaryFormatterTests.CheckSequenceEquals(@this.Keys, other.Keys) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another possible set ordering issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@krwq please review this comment
…er/corefx into SerilalizationFinishUp
|
||
public static bool IsEqual(this LinkedList<Point> @this, LinkedList<Point> other) | ||
{ | ||
if (!(@this != null && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@krwq many of these IsEqual implementations are missing this I thikn?
if (@this == null && other == null)
return true;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that's handled in CheckSequenceEquals. fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm only fixing when I understand why the value is null, in many cases it should not be null even though that would be correct to check it in here
All LGTM as of now... |
* wip: add stubs for custom isEqual * Updated blobs, small code refactoring, disabled CookieContainer test * Fixed netfx471 detection * add custom equals for graph with cycles * fix few more comparers * Merge * fix few more test cases * fix few more types, fix GetExtensionMethod to work with parameters with same type name but different generic params * finish tests (2 comparisons are commented out: search for TODO) * Adding TypeForward for DictionaryNode * Updating blobs * Update Cookie hashes, adding TypeForwards, removing unnecessary equality checks for CookieCollection * Reenabling SortedDictionary test * Update blobs * make cookie.Timestamp to roundtrip correctly * regenerate netfx and corefx blobs to account for Cookie.Timestamp change * make regex fix the blob for ObjectWithArray * fix ObjectWithArray netfx test failure * Replacing Dictionary with Hashtable for desktop serialization support * Update blobs * Fixing a lot of TypeForwards, adding another hashtable test, other fixes * Adding a few more TypeForwardedFrom attributes * Updating the coreclr version, disabling CookieContainer test * Update blobs * Disabling CompareInfo test --> waiting for coreclr * generate test cases * update blobs, comment out not working cases * fix null equality checks for new test cases and reenable them * PR feedback addressed * Fixed a typo... * Updating used coreclr version, enabling compareinfo test again
* wip: add stubs for custom isEqual * Updated blobs, small code refactoring, disabled CookieContainer test * Fixed netfx471 detection * add custom equals for graph with cycles * fix few more comparers * Merge * fix few more test cases * fix few more types, fix GetExtensionMethod to work with parameters with same type name but different generic params * finish tests (2 comparisons are commented out: search for TODO) * Adding TypeForward for DictionaryNode * Updating blobs * Update Cookie hashes, adding TypeForwards, removing unnecessary equality checks for CookieCollection * Reenabling SortedDictionary test * Update blobs * make cookie.Timestamp to roundtrip correctly * regenerate netfx and corefx blobs to account for Cookie.Timestamp change * make regex fix the blob for ObjectWithArray * fix ObjectWithArray netfx test failure * Replacing Dictionary with Hashtable for desktop serialization support * Update blobs * Fixing a lot of TypeForwards, adding another hashtable test, other fixes * Adding a few more TypeForwardedFrom attributes * Updating the coreclr version, disabling CookieContainer test * Update blobs * Disabling CompareInfo test --> waiting for coreclr * generate test cases * update blobs, comment out not working cases * fix null equality checks for new test cases and reenable them * PR feedback addressed * Fixed a typo... * Updating used coreclr version, enabling compareinfo test again
* wip: add stubs for custom isEqual * Updated blobs, small code refactoring, disabled CookieContainer test * Fixed netfx471 detection * add custom equals for graph with cycles * fix few more comparers * Merge * fix few more test cases * fix few more types, fix GetExtensionMethod to work with parameters with same type name but different generic params * finish tests (2 comparisons are commented out: search for TODO) * Adding TypeForward for DictionaryNode * Updating blobs * Update Cookie hashes, adding TypeForwards, removing unnecessary equality checks for CookieCollection * Reenabling SortedDictionary test * Update blobs * make cookie.Timestamp to roundtrip correctly * regenerate netfx and corefx blobs to account for Cookie.Timestamp change * make regex fix the blob for ObjectWithArray * fix ObjectWithArray netfx test failure * Replacing Dictionary with Hashtable for desktop serialization support * Update blobs * Fixing a lot of TypeForwards, adding another hashtable test, other fixes * Adding a few more TypeForwardedFrom attributes * Updating the coreclr version, disabling CookieContainer test * Update blobs * Disabling CompareInfo test --> waiting for coreclr * generate test cases * update blobs, comment out not working cases * fix null equality checks for new test cases and reenable them * PR feedback addressed * Fixed a typo... * Updating used coreclr version, enabling compareinfo test again
This PR is intended to be the last piece to make serialization work across Desktop and Core.
TODO
Status
Read from left to right. E.g.: Core deserializes to Core