Skip to content

Commit

Permalink
Merge pull request #1095 from pCYSl5EDgo/solution#1085
Browse files Browse the repository at this point in the history
Stop initializing members to their default values when deserializing where no data was provided for that member
  • Loading branch information
AArnott committed Nov 11, 2020
2 parents 5a6cda6 + 9b6882b commit 07615dd
Show file tree
Hide file tree
Showing 14 changed files with 2,052 additions and 1,248 deletions.
11 changes: 11 additions & 0 deletions MessagePack.sln
Expand Up @@ -88,6 +88,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Experimental",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack.Experimental.Tests", "tests\MessagePack.Experimental.Tests\MessagePack.Experimental.Tests.csproj", "{8AB40D1C-1134-4D77-B39A-19AEDC729450}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessagePack.GeneratedCode.Tests", "tests\MessagePack.GeneratedCode.Tests\MessagePack.GeneratedCode.Tests.csproj", "{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -310,6 +312,14 @@ Global
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Release|Any CPU.Build.0 = Release|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Release|NoVSIX.ActiveCfg = Release|Any CPU
{8AB40D1C-1134-4D77-B39A-19AEDC729450}.Release|NoVSIX.Build.0 = Release|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Debug|NoVSIX.ActiveCfg = Debug|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Debug|NoVSIX.Build.0 = Debug|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Release|Any CPU.Build.0 = Release|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Release|NoVSIX.ActiveCfg = Release|Any CPU
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E}.Release|NoVSIX.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -342,6 +352,7 @@ Global
{4C9BB260-62D8-49CD-9F9C-9AA6A8BFC637} = {51A614B0-E583-4DD2-AC7D-6A65634582E0}
{AC2503A7-736D-4AE6-9355-CF35D9DF6139} = {86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}
{8AB40D1C-1134-4D77-B39A-19AEDC729450} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
{D4CE7347-CEBE-46E5-BD12-1319573B6C5E} = {19FE674A-AC94-4E7E-B24C-2285D1D04CDE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B3911209-2DBF-47F8-98F6-BBC0EDFE63DE}
Expand Down
1,269 changes: 666 additions & 603 deletions sandbox/Sandbox/Generated.cs

Large diffs are not rendered by default.

149 changes: 46 additions & 103 deletions sandbox/TestData2/Generated.cs

Large diffs are not rendered by default.

60 changes: 50 additions & 10 deletions src/MessagePack.GeneratorCore/Generator/FormatterTemplate.cs
Expand Up @@ -118,18 +118,30 @@ namespace ");
this.Write(" throw new global::System.InvalidOperationException(\"typecode is n" +
"ull, struct not supported\");\r\n");
}
this.Write(" }\r\n\r\n options.Security.DepthStep(ref reader);\r\n");
this.Write(" }\r\n\r\n");
if (objInfo.MaxKey == -1 && !objInfo.HasIMessagePackSerializationCallbackReceiver) {
this.Write(" reader.Skip();\r\n return new ");
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.GetConstructorString()));
this.Write(";\r\n");
} else {
this.Write(" options.Security.DepthStep(ref reader);\r\n");
if (isFormatterResolverNecessary) {
this.Write(" global::MessagePack.IFormatterResolver formatterResolver = options.Re" +
"solver;\r\n");
}
this.Write(" var length = reader.ReadArrayHeader();\r\n");
foreach (var member in objInfo.Members) {
var canOverwrite = objInfo.ConstructorParameters.Length == 0;
if (canOverwrite) {
this.Write(" var ____result = new ");
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.GetConstructorString()));
this.Write(";\r\n");
} else { foreach (var member in objInfo.Members) {
this.Write(" var __");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write("__ = default(");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Type));
this.Write(");\r\n");
}
}
this.Write("\r\n for (int i = 0; i < length; i++)\r\n {\r\n sw" +
"itch (i)\r\n {\r\n");
Expand All @@ -138,25 +150,51 @@ namespace ");
if (member == null) { continue; }
this.Write(" case ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.IntKey));
this.Write(":\r\n __");
this.Write(":\r\n");
if (canOverwrite) {
if (member.IsWritable) {
this.Write(" ____result.");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.GetDeserializeMethodString()));
this.Write(";\r\n");
} else {
this.Write(" ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.GetDeserializeMethodString()));
this.Write(";\r\n");
}
} else {
this.Write(" __");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write("__ = ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.GetDeserializeMethodString()));
this.Write(";\r\n break;\r\n");
this.Write(";\r\n");
}
this.Write(" break;\r\n");
}
this.Write(" default:\r\n reader.Skip();\r\n " +
" break;\r\n }\r\n }\r\n\r\n var ____res" +
"ult = new ");
" break;\r\n }\r\n }\r\n\r\n");
if (!canOverwrite) {
this.Write(" var ____result = new ");
this.Write(this.ToStringHelper.ToStringWithCulture(objInfo.GetConstructorString()));
this.Write(";\r\n");
for (var memberIndex = 0; memberIndex <= objInfo.MaxKey; memberIndex++) {
bool memberAssignExists = false;
for (var memberIndex = 0; memberIndex <= objInfo.MaxKey; memberIndex++) {
var member = objInfo.GetMember(memberIndex);
if (member == null || !member.IsWritable) { continue; }
this.Write(" ____result.");
if (member == null || !member.IsWritable || objInfo.ConstructorParameters.Any(p => p.Equals(member))) { continue; }
memberAssignExists = true;
this.Write(" if (length <= ");
this.Write(this.ToStringHelper.ToStringWithCulture(memberIndex));
this.Write(")\r\n {\r\n goto MEMBER_ASSIGNMENT_END;\r\n }\r\n\r\n " +
" ____result.");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write(" = __");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write("__;\r\n");
}
if (memberAssignExists) {
this.Write("\r\n MEMBER_ASSIGNMENT_END:\r\n");
}
}

if (objInfo.HasIMessagePackSerializationCallbackReceiver) {
Expand All @@ -167,7 +205,9 @@ namespace ");
this.Write(" ____result.OnAfterDeserialize();\r\n");
}
}
this.Write(" reader.Depth--;\r\n return ____result;\r\n }\r\n }\r\n");
this.Write(" reader.Depth--;\r\n return ____result;\r\n");
}
this.Write(" }\r\n }\r\n");
}
this.Write(@"}
Expand Down
36 changes: 33 additions & 3 deletions src/MessagePack.GeneratorCore/Generator/FormatterTemplate.tt
Expand Up @@ -81,13 +81,21 @@ namespace <#= Namespace #>
<# } #>
}

<# if (objInfo.MaxKey == -1 && !objInfo.HasIMessagePackSerializationCallbackReceiver) { #>
reader.Skip();
return new <#= objInfo.GetConstructorString() #>;
<# } else { #>
options.Security.DepthStep(ref reader);
<# if (isFormatterResolverNecessary) { #>
global::MessagePack.IFormatterResolver formatterResolver = options.Resolver;
<# } #>
var length = reader.ReadArrayHeader();
<# foreach (var member in objInfo.Members) { #>
<# var canOverwrite = objInfo.ConstructorParameters.Length == 0;
if (canOverwrite) { #>
var ____result = new <#= objInfo.GetConstructorString() #>;
<# } else { foreach (var member in objInfo.Members) { #>
var __<#= member.Name #>__ = default(<#= member.Type #>);
<# } #>
<# } #>

for (int i = 0; i < length; i++)
Expand All @@ -98,7 +106,15 @@ namespace <#= Namespace #>
var member = objInfo.GetMember(memberIndex);
if (member == null) { continue; } #>
case <#= member.IntKey #>:
<# if (canOverwrite) {
if (member.IsWritable) { #>
____result.<#= member.Name #> = <#= member.GetDeserializeMethodString() #>;
<# } else { #>
<#= member.GetDeserializeMethodString() #>;
<# } #>
<# } else {#>
__<#= member.Name #>__ = <#= member.GetDeserializeMethodString() #>;
<# } #>
break;
<# } #>
default:
Expand All @@ -107,12 +123,25 @@ namespace <#= Namespace #>
}
}

<# if (!canOverwrite) { #>
var ____result = new <#= objInfo.GetConstructorString() #>;
<# for (var memberIndex = 0; memberIndex <= objInfo.MaxKey; memberIndex++) {
<# bool memberAssignExists = false;
for (var memberIndex = 0; memberIndex <= objInfo.MaxKey; memberIndex++) {
var member = objInfo.GetMember(memberIndex);
if (member == null || !member.IsWritable) { continue; } #>
if (member == null || !member.IsWritable || objInfo.ConstructorParameters.Any(p => p.Equals(member))) { continue; }
memberAssignExists = true;#>
if (length <= <#= memberIndex #>)
{
goto MEMBER_ASSIGNMENT_END;
}

____result.<#= member.Name #> = __<#= member.Name #>__;
<# } #>
<# if (memberAssignExists) { #>

MEMBER_ASSIGNMENT_END:
<# }
}

if (objInfo.HasIMessagePackSerializationCallbackReceiver) {
if (objInfo.NeedsCastOnAfter) { #>
Expand All @@ -123,6 +152,7 @@ namespace <#= Namespace #>
<# } #>
reader.Depth--;
return ____result;
<# } #>
}
}
<# } #>
Expand Down
Expand Up @@ -12,28 +12,67 @@ namespace MessagePackCompiler.Generator
{
internal static class StringKeyFormatterDeserializeHelper
{
public static string Classify(MemberSerializationInfo[] memberArray, string indent)
public static string Classify(ObjectSerializationInfo objectSerializationInfo, string indent, bool canOverwrite)
{
var memberArray = objectSerializationInfo.Members;
var buffer = new StringBuilder();
foreach (var memberInfoTuples in memberArray.Select(member => new MemberInfoTuple(member)).GroupBy(member => member.Binary.Length))
foreach (var memberInfoTuples in memberArray.Select(member => new MemberInfoTuple(member, IsConstructorParameter(objectSerializationInfo, member))).GroupBy(member => member.Binary.Length))
{
var binaryLength = memberInfoTuples.Key;
var keyLength = binaryLength >> 3;
keyLength += keyLength << 3 == binaryLength ? 0 : 1;

buffer.Append(indent).Append("case ").Append(binaryLength).Append(":\r\n");
ClassifyRecursion(buffer, indent, 1, keyLength, memberInfoTuples);
ClassifyRecursion(buffer, indent, 1, keyLength, memberInfoTuples, canOverwrite);
}

return buffer.ToString();
}

private static void Assign(StringBuilder buffer, in MemberInfoTuple member)
private static bool IsConstructorParameter(ObjectSerializationInfo objectSerializationInfo, MemberSerializationInfo member)
{
buffer.Append("__").Append(member.Info.Name).Append("__ = ").Append(member.Info.GetDeserializeMethodString()).Append(";\r\n");
foreach (var parameter in objectSerializationInfo.ConstructorParameters)
{
if (parameter.Equals(member))
{
return true;
}
}

return false;
}

private static void Assign(StringBuilder buffer, in MemberInfoTuple member, bool canOverwrite, string indent, string tab, int tabCount)
{
if (member.Info.IsWritable)
{
if (canOverwrite)
{
buffer.Append("____result.").Append(member.Info.Name).Append(" = ");
}
else
{
if (!member.IsConstructorParameter)
{
buffer.Append("__").Append(member.Info.Name).Append("__IsInitialized = true;\r\n").Append(indent);
for (var i = 0; i < tabCount; i++)
{
buffer.Append(tab);
}
}

buffer.Append("__").Append(member.Info.Name).Append("__ = ");
}

buffer.Append(member.Info.GetDeserializeMethodString()).Append(";\r\n");
}
else
{
buffer.Append("reader.Skip();\r\n");
}
}

private static void ClassifyRecursion(StringBuilder buffer, string indent, int tabCount, int keyLength, IEnumerable<MemberInfoTuple> memberCollection)
private static void ClassifyRecursion(StringBuilder buffer, string indent, int tabCount, int keyLength, IEnumerable<MemberInfoTuple> memberCollection, bool canOverwrite)
{
const string Tab = " ";
buffer.Append(indent);
Expand All @@ -46,7 +85,7 @@ private static void ClassifyRecursion(StringBuilder buffer, string indent, int t
if (memberArray.Length == 1)
{
var member = memberArray[0];
EmbedOne(buffer, indent, tabCount, member);
EmbedOne(buffer, indent, tabCount, member, canOverwrite);
return;
}

Expand Down Expand Up @@ -83,7 +122,7 @@ private static void ClassifyRecursion(StringBuilder buffer, string indent, int t
}

var member = grouping.Single();
Assign(buffer, member);
Assign(buffer, member, canOverwrite, indent, Tab, tabCount + 2);
buffer.Append(Tab + Tab).Append(indent);
for (var i = 0; i < tabCount; i++)
{
Expand All @@ -94,7 +133,7 @@ private static void ClassifyRecursion(StringBuilder buffer, string indent, int t
continue;
}

ClassifyRecursion(buffer, indent + Tab, tabCount + 1, keyLength, grouping);
ClassifyRecursion(buffer, indent + Tab, tabCount + 1, keyLength, grouping, canOverwrite);
}

buffer.Append("\r\n").Append(indent);
Expand All @@ -106,7 +145,7 @@ private static void ClassifyRecursion(StringBuilder buffer, string indent, int t
buffer.Append("}\r\n");
}

private static void EmbedOne(StringBuilder buffer, string indent, int tabCount, in MemberInfoTuple member)
private static void EmbedOne(StringBuilder buffer, string indent, int tabCount, in MemberInfoTuple member, bool canOverwrite)
{
const string Tab = " ";
var binary = member.Binary.AsSpan((tabCount - 1) << 3);
Expand Down Expand Up @@ -136,7 +175,7 @@ private static void EmbedOne(StringBuilder buffer, string indent, int tabCount,
buffer.Append(Tab);
}

Assign(buffer, member);
Assign(buffer, member, canOverwrite, indent, Tab, tabCount);
buffer.Append(indent);
for (var i = 0; i < tabCount; i++)
{
Expand Down Expand Up @@ -166,12 +205,14 @@ private static void EmbedSequenceEqual(StringBuilder buffer, MemberInfoTuple mem
internal readonly struct MemberInfoTuple : IComparable<MemberInfoTuple>
{
public readonly MemberSerializationInfo Info;
public readonly bool IsConstructorParameter;
public readonly byte[] Binary;
public readonly ulong[] Key;

public MemberInfoTuple(MemberSerializationInfo info)
public MemberInfoTuple(MemberSerializationInfo info, bool isConstructorParameter)
{
Info = info;
IsConstructorParameter = isConstructorParameter;
Binary = EmbedStringHelper.Utf8.GetBytes(info.StringKey);
ReadOnlySpan<byte> span = Binary;
var keyLength = Binary.Length >> 3;
Expand Down

0 comments on commit 07615dd

Please sign in to comment.