Comments (7)
Here is my code for handling nested types in source generator.
Generating partial class is much easier than generating partial method because generic partial method's type parameter with type constraints requires its type constraints again.
ContainingTypeInfo.cs
using Microsoft.CodeAnalysis;
using System.Text;
namespace BlazorApp1.SourceGenerator;
public readonly struct ContainingTypeInfo : IEquatable<ContainingTypeInfo>
{
public readonly string? ContainingNamespaceDisplayName;
/// <summary>
/// 0: Most inner type
/// ^1: Most outer type
/// </summary>
public readonly ContainingType[] Types;
private static int CountDepth(INamedTypeSymbol symbol, int depth, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (symbol.ContainingType is null)
{
return depth;
}
return CountDepth(symbol.ContainingType, depth + 1, cancellationToken);
}
/// <summary>
/// Collect minimum information for generating partial class.
/// </summary>
/// <param name="symbol">Must be inner type.</param>
public ContainingTypeInfo(INamedTypeSymbol symbol, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ContainingNamespaceDisplayName = symbol.ContainingNamespace?.ToDisplayString();
Types = new ContainingType[CountDepth(symbol, 1, cancellationToken)];
Types[0] = new(symbol, cancellationToken);
for (int index = 1; symbol.ContainingType is { } containingTypeSymbol; index++)
{
cancellationToken.ThrowIfCancellationRequested();
Types[index] = new(containingTypeSymbol, cancellationToken);
symbol = containingTypeSymbol;
}
}
public ContainingTypeInfo()
{
ContainingNamespaceDisplayName = null;
Types = [];
}
public StringBuilder AppendFullName(StringBuilder? builder = default)
{
builder ??= new StringBuilder(64);
if (ContainingNamespaceDisplayName is not null)
{
builder.Append(ContainingNamespaceDisplayName);
builder.Append('.');
}
for (int i = 0; i < Types.Length;)
{
builder.Append(Types[i].Name);
if (++i < Types.Length)
{
builder.Append('.');
}
}
return builder;
}
public override bool Equals(object? other)
{
if (other is not ContainingTypeInfo value)
{
return false;
}
return Equals(in value);
}
public bool Equals(ContainingTypeInfo other)
{
return Equals(in other);
}
public bool Equals(in ContainingTypeInfo other)
{
if (ContainingNamespaceDisplayName != other.ContainingNamespaceDisplayName)
{
return false;
}
if (Types.Length != other.Types.Length)
{
return false;
}
for (int i = 0; i < Types.Length; i++)
{
if (!Types[i].Equals(in other.Types[i]))
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
return ((ContainingNamespaceDisplayName?.Length ?? 0) << 16) | (Types.Length);
}
public readonly struct ContainingType : IEquatable<ContainingType>
{
public readonly string Name;
public readonly bool IsStatic;
public readonly bool IsValueType;
public readonly bool IsRecord;
public readonly string[] TypeParameters;
public ContainingType(INamedTypeSymbol symbol, CancellationToken cancellationToken)
{
Name = symbol.Name;
IsStatic = symbol.IsStatic;
IsValueType = symbol.IsValueType;
IsRecord = symbol.IsRecord;
if (symbol.TypeParameters.Length == 0)
{
TypeParameters = [];
return;
}
TypeParameters = new string[symbol.TypeParameters.Length];
for (int i = 0; i < TypeParameters.Length; i++)
{
cancellationToken.ThrowIfCancellationRequested();
TypeParameters[i] = symbol.TypeParameters[i].Name;
}
}
public override bool Equals(object? other)
{
if (other is not ContainingType value)
{
return false;
}
return Equals(in value);
}
public bool Equals(ContainingType other)
{
return Equals(in other);
}
public bool Equals(in ContainingType other)
{
if (Name != other.Name)
{
return false;
}
if (IsStatic != other.IsStatic)
{
return false;
}
if (IsValueType != other.IsValueType)
{
return false;
}
if (IsRecord != other.IsRecord)
{
return false;
}
if (!ReferenceEquals(TypeParameters, other.TypeParameters))
{
if (TypeParameters.Length != other.TypeParameters.Length)
{
return false;
}
for (int i = 0; i < TypeParameters.Length; i++)
{
if (TypeParameters[i] != other.TypeParameters[i])
{
return false;
}
}
}
return true;
}
public override int GetHashCode()
{
return (TypeParameters.Length << 16) | (Name.Length << 3) | ((IsStatic ? 1 : 0) << 2) | ((IsValueType ? 1 : 0) << 1) | (IsRecord ? 1 : 0);
}
public static bool operator ==(in ContainingType left, in ContainingType right)
{
return left.Equals(in right);
}
public static bool operator !=(in ContainingType left, in ContainingType right)
{
return !left.Equals(in right);
}
}
public static bool operator ==(in ContainingTypeInfo left, in ContainingTypeInfo right)
{
return left.Equals(in right);
}
public static bool operator !=(in ContainingTypeInfo left, in ContainingTypeInfo right)
{
return !left.Equals(in right);
}
}
from messagepack-csharp.
MessagePack attributes that the analyzer responds to only requires a reference to the MessagePack.Annotations
package. But it takes a reference to the MessagePack
package to actually build a formatter. So the source generator can only do its work if the MessagePack
assembly is also referenced. Assemblies that only reference the annotations will be limited to dynamically generated formatters at runtime.
from messagepack-csharp.
I have the changes ready for this. I'll wait to send the pull request until after #1736 has merged, since the changes are based on that.
from messagepack-csharp.
What about private member access? Should formatters be generated as private classes of the formatted type to gain access?
We should add an override switch to MessagePackSerializerOptions to force use of dynamic formatters.
from messagepack-csharp.
We should add an override switch to MessagePackSerializerOptions to force use of dynamic formatters.
Actually that doesn't work, because DynamicObjectResolver
, which would need to read the option, isn't given a chance to read the option per the API. And it's incompatible with the FormatterCache<T>
which takes no parameters except the type parameter.
But maybe that's a good thing anyway, because it should really be the producer of the type (or formatter) to decide which formatter to use -- not the consumer. If I ship a library, it's my responsibility to ensure that any formatters it ships with (whether manually written or source generated) actually work, and then my consumer can use it.
from messagepack-csharp.
Maybe the better fix for this is to not overload DynamicObjectResolver
to support pre-compiled formatters, but rather to insert a new resolver in the standard resolver chain (in front of the DynamicObjectResolver). It still won't allow for options-based selection of formatters, but at least it will be something that can be included or omitted from the resolver chain on an as-needed basis.
from messagepack-csharp.
On the subject of AOT formatters that can access private members via being a nested class, I think that'll work. But we'll have to take care for the generated code to handle multiple levels of nesting. e.g. the type that needs a formatter may itself be a nested type.
from messagepack-csharp.
Related Issues (20)
- Source generate formatters as nested under the types they format for private member access HOT 7
- Serialization of WeakReference<T> HOT 4
- Question: Possibility of exposing LZ4 functionality? HOT 1
- ThreadsafeTypeKeyHashTable.VolatileWrite missing nullable declaration for `value` parameter HOT 2
- MessagePack.Generator - Issues with generic
- Private fields on base class do not get serialized HOT 4
- MessagePack.Generator.2.5.140 - Union metatag causes msc.exe error: Index was outside the bounds of the array
- Add parameter list `ref` usage analyzer for low level reader/writer HOT 1
- Fail in application running on MessagepackCompiler.RunAsync on Windows (works on Linux & macOS) HOT 5
- messagepack is missing NuGet package README file
- MsgPack003 does not generate error in all cases HOT 5
- Update readme file for Analyzer HOT 3
- Add Support for external types via class libraries HOT 1
- MsgPack003 errors showing in Rider for type with custom formatter HOT 5
- Typeless serializer fails to serialize simple type, unless fields are prefixed with "_" HOT 2
- Explicit interface implementation
- Help request - using different (derived-)types for serialize/deserialize (IMessagePackFormatter<Tser, Tdes>)? HOT 2
- Discrepancy in Serialized Size with MessagePack for Complex Object Graphs HOT 1
- Serialize/deserialize of interface types failing with ContractlessStandardResolver HOT 1
- Make System.Runtime.CompilerServices.Unsafe a conditional based on TFM
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from messagepack-csharp.