stride3d / stride

Stride (formerly Xenko), a free and open-source cross-platform C# game engine.
https://stride3d.net
MIT License
6.65k stars 957 forks source link

fix: Cecil serialization inheritance cutoff with complex base types #2457

Closed Eideren closed 2 months ago

Eideren commented 2 months ago

PR Details

The binary serializer can lose serialized data when round-tripped with a specific inheritance setup;

[DataContract]
public class D : C<AnyType>{ public bool bill; }

public class C<T> : B<T>{}
public class B<T> : A{}

[DataContract(Inherited = true)]
public class A : EntityComponent{}

The following logic fails to capture B<T> https://github.com/stride3d/stride/blob/37bbf0c85262711f56786319effab2621479bab9/sources/core/Stride.Core.AssemblyProcessor/Serializers/CecilSerializerContext.cs#L198-L204 Which in turn means that all serialized data contained in its base types would be discarded, most obvious with the component Id inherited from EntityComponent, which right now shows up as a new Id on every save caused by the asset collision Id de-duplication using binary serialization through the Clone function: https://github.com/stride3d/stride/blob/37bbf0c85262711f56786319effab2621479bab9/sources/assets/Stride.Core.Assets/Analysis/AssetCollision.cs#L33-L48

Related Issue

Kind of hard to tell

Types of changes

Checklist

Kryptos-FR commented 2 months ago

Nice solution. Just a question - the check on generic type inside the for loop where we continue - what scenario is that skipping?

Would be nice to add a few comments on those lines that explain what the algorithm is doing.

Eideren commented 2 months ago

Added a couple of comments to clarify. @manio143 from what I can tell, this is a fallback for when ResolveGenericsVisitor.Process failed to resolve, which afaict happens when trying to serialize B as an open type with an inheritance like the following:

class A<T>{}
class B<T> : A<T>{}

Not 100% sure as I haven't tested it, here's the source for that method: https://github.com/stride3d/stride/blob/beb2972d0ed333068f226db6773d225dcad83056/sources/core/Stride.Core.AssemblyProcessor/ResolveGenericsVisitor.cs#L27-L44

xen2 commented 2 months ago

Would it be easy to add a test (in https://github.com/stride3d/stride/blob/master/sources/core/Stride.Core.Tests/TestSerialization.cs) so that we make sure to not break it in the future? (i.e. if we switch to Roslyn) If not trivial, no problem.

Eideren commented 2 months ago

Looked into it - in that particular test context, cecil fails to generate a serializer for the class even with the exact same type definitions I used at edit and runtime to validate the changes in this PR. I don't have a lot of time to dig too deep into why the test fails while the real one passes, so I'll have to leave it as is for now

xen2 commented 2 months ago

Thanks for giving it a try, all good then!