diff --git a/Import/GacUI.h b/Import/GacUI.h index 26ed8fae..59af3490 100644 --- a/Import/GacUI.h +++ b/Import/GacUI.h @@ -25304,8 +25304,16 @@ extern int SetupRawWindowsDirect2DRenderer(); // Gtk extern int SetupGtkRenderer(); +// Wayland +namespace vl::presentation::elements::wgac +{ + extern int SetupWGacRenderer(); + extern int SetupWGacHostedRenderer(); +} + // macOS extern int SetupOSXCoreGraphicsRenderer(); +extern int SetupOSXHostedCoreGraphicsRenderer(); #endif diff --git a/Import/VlppWorkflowCompiler.cpp b/Import/VlppWorkflowCompiler.cpp index 895c88b9..502bdf28 100644 --- a/Import/VlppWorkflowCompiler.cpp +++ b/Import/VlppWorkflowCompiler.cpp @@ -24352,6 +24352,22 @@ GenerateAssembly assembly->typeImpl->enums.Add(tdEnum); } } + + // Sort custom types by type name for deterministic serialization order + auto sortByTypeName = [](auto& list) + { + if (list.Count() > 1) + { + collections::Sort(&list[0], list.Count(), [](const auto& a, const auto& b) + { + return a->GetTypeName() <=> b->GetTypeName(); + }); + } + }; + sortByTypeName(assembly->typeImpl->classes); + sortByTypeName(assembly->typeImpl->interfaces); + sortByTypeName(assembly->typeImpl->structs); + sortByTypeName(assembly->typeImpl->enums); } for (auto module : manager->GetModules()) diff --git a/Import/VlppWorkflowRuntime.cpp b/Import/VlppWorkflowRuntime.cpp index 93888fc1..ef119b2b 100644 --- a/Import/VlppWorkflowRuntime.cpp +++ b/Import/VlppWorkflowRuntime.cpp @@ -679,6 +679,36 @@ namespace vl using WfReader = Reader>; using WfWriter = Writer>; +/*********************************************************************** +Serialization (Sort Keys for Deterministic Binary Serialization) +***********************************************************************/ + + static WString GetMethodSortKey(IMethodInfo* mi) + { + auto td = mi->GetOwnerTypeDescriptor(); + auto group = mi->GetOwnerMethodGroup(); + WString name = (group == td->GetConstructorGroup()) ? WString(L"#ctor") : mi->GetName(); + WString key = td->GetTypeName() + L"::" + name + L"("; + vint paramCount = mi->GetParameterCount(); + for (vint i = 0; i < paramCount; i++) + { + if (i > 0) key += L","; + key += mi->GetParameter(i)->GetType()->GetTypeFriendlyName(); + } + key += L")"; + return key; + } + + static WString GetPropertySortKey(IPropertyInfo* pi) + { + return pi->GetOwnerTypeDescriptor()->GetTypeName() + L"::" + pi->GetName(); + } + + static WString GetEventSortKey(IEventInfo* ei) + { + return ei->GetOwnerTypeDescriptor()->GetTypeName() + L"::" + ei->GetName(); + } + /*********************************************************************** Serialization (CollectMetadata) ***********************************************************************/ @@ -2206,21 +2236,79 @@ Serialization (Assembly) CollectMetadata(value.typeImpl.Obj(), prepare); } CollectMetadata(value.instructions, prepare); - for (vint i = prepare.tds.Count() - 1; i >= 0; i--) + + // Copy collected metadata to Lists and sort deterministically by name + // instead of using pointer-sorted SortedLists which vary between runs + List sortedTds; + for (auto td : prepare.tds) { - if (writer.context->tdIndex.Keys().Contains(prepare.tds[i])) + if (!writer.context->tdIndex.Keys().Contains(td)) { - prepare.tds.RemoveAt(i); + sortedTds.Add(td); } } - writer.context->Initialize(prepare); + if (sortedTds.Count() > 0) + { + Sort(&sortedTds[0], sortedTds.Count(), [](ITypeDescriptor* a, ITypeDescriptor* b) + { + return a->GetTypeName() <=> b->GetTypeName(); + }); + } - vint tdCount = prepare.tds.Count(); - vint miCount = prepare.mis.Count(); - vint piCount = prepare.pis.Count(); - vint eiCount = prepare.eis.Count(); + List sortedMis; + CopyFrom(sortedMis, prepare.mis); + if (sortedMis.Count() > 0) + { + Sort(&sortedMis[0], sortedMis.Count(), [](IMethodInfo* a, IMethodInfo* b) + { + return GetMethodSortKey(a) <=> GetMethodSortKey(b); + }); + } + + List sortedPis; + CopyFrom(sortedPis, prepare.pis); + if (sortedPis.Count() > 0) + { + Sort(&sortedPis[0], sortedPis.Count(), [](IPropertyInfo* a, IPropertyInfo* b) + { + return GetPropertySortKey(a) <=> GetPropertySortKey(b); + }); + } + + List sortedEis; + CopyFrom(sortedEis, prepare.eis); + if (sortedEis.Count() > 0) + { + Sort(&sortedEis[0], sortedEis.Count(), [](IEventInfo* a, IEventInfo* b) + { + return GetEventSortKey(a) <=> GetEventSortKey(b); + }); + } + + // Build writer context indices from deterministically-sorted lists + for (auto [td, index] : indexed(sortedTds)) + { + writer.context->tdIndex.Add(td, index); + } + for (auto [mi, index] : indexed(sortedMis)) + { + writer.context->miIndex.Add(mi, index); + } + for (auto [pi, index] : indexed(sortedPis)) + { + writer.context->piIndex.Add(pi, index); + } + for (auto [ei, index] : indexed(sortedEis)) + { + writer.context->eiIndex.Add(ei, index); + } + + vint tdCount = sortedTds.Count(); + vint miCount = sortedMis.Count(); + vint piCount = sortedPis.Count(); + vint eiCount = sortedEis.Count(); writer << tdCount << miCount << piCount << eiCount; - for (auto td : prepare.tds) + for (auto td : sortedTds) { writer << td; } @@ -2231,15 +2319,15 @@ Serialization (Assembly) GetGlobalTypeManager()->AddTypeLoader(value.typeImpl); } - for (auto mi : prepare.mis) + for (auto mi : sortedMis) { writer << mi; } - for (auto pi : prepare.pis) + for (auto pi : sortedPis) { writer << pi; } - for (auto ei : prepare.eis) + for (auto ei : sortedEis) { writer << ei; } diff --git a/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x64 b/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x64 index 6a503203..37dfa659 100644 Binary files a/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x64 and b/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x64 differ diff --git a/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x86 b/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x86 index b88cdded..8cf1f129 100644 Binary files a/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x86 and b/Tutorial/GacUI_HelloWorlds/UIRes/Xml.bin.x86 differ