Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public record MemberSerializationInfo(
bool IsProperty,
bool IsWritable,
bool IsReadable,
bool IsInitOnly,
bool IsRequired,
int IntKey,
string StringKey,
string Name,
Expand All @@ -16,6 +18,8 @@ public record MemberSerializationInfo(
{
private static readonly IReadOnlyCollection<string> PrimitiveTypes = new HashSet<string>(AnalyzerUtilities.PrimitiveTypes);

public string LocalVariableName => $"__{this.Name}__";

public string GetSerializeMethodString()
{
if (CustomFormatter is not null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.CodeAnalysis;

namespace MessagePack.SourceGenerator.CodeAnalysis;
Expand All @@ -16,6 +16,13 @@ public record ObjectSerializationInfo : ResolverRegisterInfo

public required MemberSerializationInfo[] ConstructorParameters { get; init; }

/// <summary>
/// Gets the members that are either init-only properties or required (and therefore must appear in an object initializer).
/// </summary>
public required MemberSerializationInfo[] InitMembers { get; init; }

public bool MustDeserializeFieldsFirst => this.ConstructorParameters.Length > 0 || this.InitMembers.Length > 0;

public required bool IsIntKey { get; init; }

public required MemberSerializationInfo[] Members { get; init; }
Expand Down Expand Up @@ -76,6 +83,7 @@ public static ObjectSerializationInfo Create(
IncludesPrivateMembers = includesPrivateMembers,
GenericTypeParameters = genericTypeParameters,
ConstructorParameters = constructorParameters,
InitMembers = members.Where(x => ((x.IsProperty && x.IsInitOnly) || x.IsRequired) && !constructorParameters.Contains(x)).ToArray(),
IsIntKey = isIntKey,
Members = members,
HasIMessagePackSerializationCallbackReceiver = hasIMessagePackSerializationCallbackReceiver,
Expand All @@ -91,8 +99,47 @@ public static ObjectSerializationInfo Create(

public string GetConstructorString()
{
var args = string.Join(", ", this.ConstructorParameters.Select(x => "__" + x.Name + "__"));
return $"{this.DataType.GetQualifiedName(Qualifiers.GlobalNamespace, GenericParameterStyle.Identifiers)}({args})";
StringBuilder builder = new();
builder.Append(this.DataType.GetQualifiedName(Qualifiers.GlobalNamespace, GenericParameterStyle.Identifiers));

builder.Append('(');

for (int i = 0; i < this.ConstructorParameters.Length; i++)
{
if (i != 0)
{
builder.Append(", ");
}

builder.Append(this.ConstructorParameters[i].LocalVariableName);
}

builder.Append(')');

if (this.InitMembers.Length > 0)
{
builder.Append(" { ");

for (int i = 0; i < this.InitMembers.Length; i++)
{
if (i != 0)
{
builder.Append(", ");
}

// Strictly speaking, we should only be assigning these init-only properties if values for them
// was provided in the deserialized stream.
// However the C# language does not provide a means to do this, so we always assign them.
// https://github.com/dotnet/csharplang/issues/6117
builder.Append(this.InitMembers[i].Name);
builder.Append(" = ");
builder.Append(this.InitMembers[i].LocalVariableName);
}

builder.Append(" }");
}

return builder.ToString();
}

public virtual bool Equals(ObjectSerializationInfo? other)
Expand Down
23 changes: 13 additions & 10 deletions src/MessagePack.SourceGenerator/CodeAnalysis/TypeCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma warning disable SA1649 // File name should match first type name

using System.Collections.Immutable;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand Down Expand Up @@ -731,20 +732,21 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
continue;
}

var isReadable = item.GetMethod is not null;
var isWritable = item.SetMethod is not null;
bool isReadable = item.GetMethod is not null;
bool isWritable = item.SetMethod is not null;
if (!isReadable && !isWritable)
{
continue;
}

bool isInitOnly = item.SetMethod?.IsInitOnly is true;
AttributeData? keyAttribute = attributes.FirstOrDefault(attributes => attributes.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute));
string stringKey = keyAttribute?.ConstructorArguments.Length == 1 && keyAttribute.ConstructorArguments[0].Value is string name ? name : item.Name;

includesPrivateMembers |= item.GetMethod is not null && !IsAllowedAccessibility(item.GetMethod.DeclaredAccessibility);
includesPrivateMembers |= item.SetMethod is not null && !IsAllowedAccessibility(item.SetMethod.DeclaredAccessibility);
FormatterDescriptor? specialFormatter = GetSpecialFormatter(item);
MemberSerializationInfo member = new(true, isWritable, isReadable, hiddenIntKey++, stringKey, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
MemberSerializationInfo member = new(true, isWritable, isReadable, isInitOnly, item.IsRequired, hiddenIntKey++, stringKey, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
stringMembers.Add(member.StringKey, (member, item.Type));

if (specialFormatter is null)
Expand All @@ -768,7 +770,7 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
string stringKey = keyAttribute?.ConstructorArguments.Length == 1 && keyAttribute.ConstructorArguments[0].Value is string name ? name : item.Name;

FormatterDescriptor? specialFormatter = GetSpecialFormatter(item);
MemberSerializationInfo member = new(false, IsWritable: !item.IsReadOnly, IsReadable: true, hiddenIntKey++, stringKey, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
MemberSerializationInfo member = new(false, IsWritable: !item.IsReadOnly, IsReadable: true, IsInitOnly: false, item.IsRequired, hiddenIntKey++, stringKey, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
stringMembers.Add(member.StringKey, (member, item.Type));
if (specialFormatter is null)
{
Expand Down Expand Up @@ -803,8 +805,9 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
continue;
}

var isReadable = item.GetMethod is not null;
var isWritable = item.SetMethod is not null;
bool isReadable = item.GetMethod is not null;
bool isWritable = item.SetMethod is not null;
bool isInitOnly = item.SetMethod?.IsInitOnly is true;
if (!isReadable && !isWritable)
{
continue;
Expand Down Expand Up @@ -870,7 +873,7 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
this.reportDiagnostic?.Invoke(Diagnostic.Create(MsgPack00xMessagePackAnalyzer.KeysMustBeUnique, item.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax().GetLocation()));
}

var member = new MemberSerializationInfo(true, isWritable, isReadable, intKey!.Value, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
var member = new MemberSerializationInfo(true, isWritable, isReadable, isInitOnly, item.IsRequired, intKey!.Value, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
intMembers.Add(member.IntKey, (member, item.Type));
}
else if (stringKey is not null)
Expand All @@ -880,7 +883,7 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
this.reportDiagnostic?.Invoke(Diagnostic.Create(MsgPack00xMessagePackAnalyzer.KeysMustBeUnique, item.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax().GetLocation()));
}

var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, stringKey!, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
var member = new MemberSerializationInfo(true, isWritable, isReadable, isInitOnly, item.IsRequired, hiddenIntKey++, stringKey!, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
stringMembers.Add(member.StringKey, (member, item.Type));
}
}
Expand Down Expand Up @@ -957,7 +960,7 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
this.reportDiagnostic?.Invoke(Diagnostic.Create(MsgPack00xMessagePackAnalyzer.KeysMustBeUnique, item.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax().GetLocation()));
}

var member = new MemberSerializationInfo(true, IsWritable: !item.IsReadOnly, IsReadable: true, intKey!.Value, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
var member = new MemberSerializationInfo(true, IsWritable: !item.IsReadOnly, IsReadable: true, IsInitOnly: false, item.IsRequired, intKey!.Value, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
intMembers.Add(member.IntKey, (member, item.Type));
}
else
Expand All @@ -967,7 +970,7 @@ private bool CheckValidMessagePackFormatterAttribute(AttributeData formatterAttr
this.reportDiagnostic?.Invoke(Diagnostic.Create(MsgPack00xMessagePackAnalyzer.KeysMustBeUnique, item.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax().GetLocation()));
}

var member = new MemberSerializationInfo(true, IsWritable: !item.IsReadOnly, IsReadable: true, hiddenIntKey++, stringKey!, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
var member = new MemberSerializationInfo(true, IsWritable: !item.IsReadOnly, IsReadable: true, IsInitOnly: false, item.IsRequired, hiddenIntKey++, stringKey!, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), specialFormatter);
stringMembers.Add(member.StringKey, (member, item.Type));
}
}
Expand Down
46 changes: 22 additions & 24 deletions src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,18 @@ public virtual string TransformText()
this.Write("\t\t\tMsgPack::IFormatterResolver formatterResolver = options.Resolver;\r\n");
}
this.Write("\t\t\tvar length = reader.ReadArrayHeader();\r\n");
var canOverwrite = Info.ConstructorParameters.Length == 0;
if (canOverwrite) {
if (Info.MustDeserializeFieldsFirst) {
foreach (var member in Info.Members) {
this.Write("\t\t\tvar ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.LocalVariableName));
this.Write(" = default(");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Type));
this.Write(");\r\n");
}
} else {
this.Write("\t\t\tvar ____result = new ");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.GetConstructorString()));
this.Write(";\r\n");
} else { foreach (var member in Info.Members) {
this.Write("\t\t\tvar __");
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\t\t\tfor (int i = 0; i < length; i++)\r\n\t\t\t{\r\n\t\t\t\tswitch (i)\r\n\t\t\t\t{\r\n");
for (var memberIndex = 0; memberIndex <= Info.MaxKey; memberIndex++) {
Expand All @@ -127,44 +127,42 @@ public virtual string TransformText()
this.Write("\t\t\t\t\tcase ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.IntKey));
this.Write(":\r\n");
if (canOverwrite) {
if (Info.MustDeserializeFieldsFirst) {
this.Write("\t\t\t\t\t\t");
this.Write(this.ToStringHelper.ToStringWithCulture(member.LocalVariableName));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.GetDeserializeMethodString()));
this.Write(";\r\n");
} else {
if (member.IsWritable) {
this.Write("\t\t\t\t\t\t____result.");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.GetDeserializeMethodString()));
this.Write(";\r\n");
} else {
this.Write("\t\t\t\t\t\t");
this.Write(this.ToStringHelper.ToStringWithCulture(member.GetDeserializeMethodString()));
this.Write(";\r\n");
this.Write("\t\t\t\t\t\treader.Skip();\r\n");
}
} else {
this.Write("\t\t\t\t\t\t__");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write("__ = ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.GetDeserializeMethodString()));
this.Write(";\r\n");
}
this.Write("\t\t\t\t\t\tbreak;\r\n");
}
this.Write("\t\t\t\t\tdefault:\r\n\t\t\t\t\t\treader.Skip();\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n");
if (!canOverwrite) {
if (Info.MustDeserializeFieldsFirst) {
this.Write("\t\t\tvar ____result = new ");
this.Write(this.ToStringHelper.ToStringWithCulture(Info.GetConstructorString()));
this.Write(";\r\n");
bool memberAssignExists = false;
for (var memberIndex = 0; memberIndex <= Info.MaxKey; memberIndex++) {
var member = Info.GetMember(memberIndex);
if (member == null || !member.IsWritable || Info.ConstructorParameters.Any(p => p.Equals(member))) { continue; }
if (member == null || !member.IsWritable || member.IsInitOnly || member.IsRequired || Info.ConstructorParameters.Any(p => p.Equals(member))) { continue; }
memberAssignExists = true;
this.Write("\t\t\tif (length <= ");
this.Write(this.ToStringHelper.ToStringWithCulture(memberIndex));
this.Write(")\r\n\t\t\t{\r\n\t\t\t\tgoto MEMBER_ASSIGNMENT_END;\r\n\t\t\t}\r\n\r\n\t\t\t____result.");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write(" = __");
this.Write(this.ToStringHelper.ToStringWithCulture(member.Name));
this.Write("__;\r\n");
this.Write(" = ");
this.Write(this.ToStringHelper.ToStringWithCulture(member.LocalVariableName));
this.Write(";\r\n");
}
if (memberAssignExists) {
this.Write("\r\n\t\tMEMBER_ASSIGNMENT_END:\r\n");
Expand Down
26 changes: 13 additions & 13 deletions src/MessagePack.SourceGenerator/Transforms/FormatterTemplate.tt
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ using MsgPack = global::MessagePack;
MsgPack::IFormatterResolver formatterResolver = options.Resolver;
<# } #>
var length = reader.ReadArrayHeader();
<# var canOverwrite = Info.ConstructorParameters.Length == 0;
if (canOverwrite) { #>
<# if (Info.MustDeserializeFieldsFirst) {
foreach (var member in Info.Members) { #>
var <#= member.LocalVariableName #> = default(<#= member.Type #>);
<# }
} else { #>
var ____result = new <#= Info.GetConstructorString() #>;
<# } else { foreach (var member in Info.Members) { #>
var __<#= member.Name #>__ = default(<#= member.Type #>);
<# } #>
<# } #>

for (int i = 0; i < length; i++)
Expand All @@ -91,14 +91,14 @@ using MsgPack = global::MessagePack;
var member = Info.GetMember(memberIndex);
if (member == null) { continue; } #>
case <#= member.IntKey #>:
<# if (canOverwrite) {
<# if (Info.MustDeserializeFieldsFirst) { #>
<#= member.LocalVariableName #> = <#= member.GetDeserializeMethodString() #>;
<# } else {
if (member.IsWritable) { #>
____result.<#= member.Name #> = <#= member.GetDeserializeMethodString() #>;
<# } else { #>
<#= member.GetDeserializeMethodString() #>;
reader.Skip();
<# } #>
<# } else {#>
__<#= member.Name #>__ = <#= member.GetDeserializeMethodString() #>;
<# } #>
break;
<# } #>
Expand All @@ -108,19 +108,19 @@ using MsgPack = global::MessagePack;
}
}

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private static void Assign(StringBuilder buffer, in MemberInfoTuple member, bool
}
else
{
if (!member.IsConstructorParameter)
if (!member.IsConstructorParameter && !member.Info.IsInitOnly && !member.Info.IsRequired)
{
buffer.Append("__").Append(member.Info.Name).Append("__IsInitialized = true;\r\n").Append(indent);
for (var i = 0; i < tabCount; i++)
Expand Down
Loading