Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop initializing members to their default values when deserializing where no data was provided for that member #1095

Merged
merged 2 commits into from Nov 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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