Skip to content

Commit e243a0f

Browse files
gunpal5Gunpal JainHavenDV
authored
fix: NativeAOT/Trimming for generated tools (#20)
* fix: NativeAOT/Trimming for generated tools * fixed ComplexClassSerializerTools.cs * duplicate decleration * duplicate decleration * fix: property case insensitive * removed System.Text.Json dependency * test * test * test: Fixed --------- Co-authored-by: Gunpal Jain <[email protected]> Co-authored-by: Konstantin S <[email protected]>
1 parent 5e3992a commit e243a0f

File tree

123 files changed

+14735
-98
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

123 files changed

+14735
-98
lines changed

CSharpToJsonSchema.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpToJsonSchema.UnitTest
4040
EndProject
4141
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpToJsonSchema.IntegrationTests", "src\tests\CSharpToJsonSchema.IntegrationTests\CSharpToJsonSchema.IntegrationTests.csproj", "{247C813A-9072-4DF3-B403-B35E3688DB4B}"
4242
EndProject
43+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpToJsonSchema.AotTests", "src\tests\CSharpToJsonSchema.AotTests\CSharpToJsonSchema.AotTests.csproj", "{6167F915-83EB-42F9-929B-AD4719A55811}"
44+
EndProject
4345
Global
4446
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4547
Debug|Any CPU = Debug|Any CPU
@@ -70,6 +72,10 @@ Global
7072
{247C813A-9072-4DF3-B403-B35E3688DB4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
7173
{247C813A-9072-4DF3-B403-B35E3688DB4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
7274
{247C813A-9072-4DF3-B403-B35E3688DB4B}.Release|Any CPU.Build.0 = Release|Any CPU
75+
{6167F915-83EB-42F9-929B-AD4719A55811}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
76+
{6167F915-83EB-42F9-929B-AD4719A55811}.Debug|Any CPU.Build.0 = Debug|Any CPU
77+
{6167F915-83EB-42F9-929B-AD4719A55811}.Release|Any CPU.ActiveCfg = Release|Any CPU
78+
{6167F915-83EB-42F9-929B-AD4719A55811}.Release|Any CPU.Build.0 = Release|Any CPU
7379
EndGlobalSection
7480
GlobalSection(SolutionProperties) = preSolution
7581
HideSolutionNode = FALSE
@@ -82,6 +88,7 @@ Global
8288
{8BDEB07E-C8C8-436C-B736-F3C093CB27AD} = {AAA11B78-2764-4520-A97E-46AA7089A588}
8389
{8AFCC30C-C59D-498D-BE68-9A328B3E5599} = {AAA11B78-2764-4520-A97E-46AA7089A588}
8490
{247C813A-9072-4DF3-B403-B35E3688DB4B} = {AAA11B78-2764-4520-A97E-46AA7089A588}
91+
{6167F915-83EB-42F9-929B-AD4719A55811} = {AAA11B78-2764-4520-A97E-46AA7089A588}
8592
EndGlobalSection
8693
GlobalSection(ExtensibilityGlobals) = postSolution
8794
SolutionGuid = {CED9A020-DBA5-4BE6-8096-75E528648EC1}

src/libs/CSharpToJsonSchema.Generators/CSharpToJsonSchema.Generators.csproj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
@@ -7,10 +7,11 @@
77
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
88
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
99
<NoWarn>$(NoWarn);CA1014;CA1031;CA1308</NoWarn>
10+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1011
</PropertyGroup>
1112

1213
<ItemGroup Label="Global Usings">
13-
<Using Include="System.Net.Http"/>
14+
<Using Include="System.Net.Http" />
1415
</ItemGroup>
1516

1617
<ItemGroup>
@@ -22,8 +23,9 @@
2223
<PrivateAssets>all</PrivateAssets>
2324
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2425
</PackageReference>
25-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all"/>
26-
<PackageReference Include="H.Generators.Extensions" Version="1.24.2" PrivateAssets="all"/>
26+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all" />
27+
<PackageReference Include="H.Generators.Extensions" Version="1.24.2" PrivateAssets="all" />
28+
2729
</ItemGroup>
2830

2931
</Project>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using CSharpToJsonSchema.Generators.JsonGen.Helpers;
2+
using CSharpToJsonSchema.Generators.Models;
3+
using H.Generators.Extensions;
4+
5+
namespace CSharpToJsonSchema.Generators.Conversion;
6+
7+
using System;
8+
using System.Linq;
9+
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.CSharp;
11+
using Microsoft.CodeAnalysis.CSharp.Syntax;
12+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
13+
14+
public static class SymbolGenerator
15+
{
16+
public static ITypeSymbol? GenerateParameterBasedClassSymbol(
17+
string rootNamespace,
18+
IMethodSymbol methodSymbol,
19+
Compilation originalCompilation)
20+
{
21+
22+
23+
// Example: we create a class name
24+
var className = $"{methodSymbol.Name}Args";
25+
26+
// Build property declarations based on method parameters
27+
var properties = methodSymbol
28+
.Parameters
29+
.Where(s=>s.Type.Name != "CancellationToken")
30+
.Select(p =>
31+
{
32+
33+
var propertyTypeName = p.Type.ToDisplayString(); // or another approach
34+
var decleration = SyntaxFactory.PropertyDeclaration(
35+
SyntaxFactory.ParseTypeName(propertyTypeName),
36+
p.Name.ToPropertyName())
37+
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
38+
.WithAccessorList(
39+
SyntaxFactory.AccessorList(
40+
SyntaxFactory.List(new[]
41+
{
42+
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
43+
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
44+
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
45+
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
46+
})
47+
)
48+
);
49+
if (p.Type.TypeKind == TypeKind.Enum)
50+
{
51+
//decleration = decleration.AddAttributeLists(AttributeList(SingletonSeparatedList(GetConverter(propertyTypeName))));
52+
}
53+
return decleration;
54+
})
55+
.ToArray();
56+
57+
// Build a class declaration
58+
var classDecl = SyntaxFactory.ClassDeclaration(className)
59+
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
60+
.AddMembers(properties);
61+
62+
// We create a compilation unit holding our new class
63+
64+
65+
66+
var namespaceName =rootNamespace; // choose your own
67+
var ns = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.IdentifierName(namespaceName))
68+
.AddMembers(classDecl);
69+
70+
var compilationUnit = SyntaxFactory.CompilationUnit()
71+
.AddMembers(ns) // if ns is a NamespaceDeclarationSyntax
72+
.NormalizeWhitespace();
73+
74+
var parseOptions = CSharpParseOptions.Default.WithLanguageVersion(originalCompilation.GetLanguageVersion()?? LanguageVersion.Default);
75+
var syntaxTree =CSharpSyntaxTree.Create(compilationUnit,parseOptions);
76+
//CSharpSyntaxTree.Create(ns.NormalizeWhitespace());
77+
78+
// Now we need to add this syntax tree to a new or existing compilation
79+
var assemblyName = "TemporaryAssembly";
80+
var compilation = originalCompilation
81+
.AddSyntaxTrees(syntaxTree);
82+
//.WithAssemblyName(assemblyName);
83+
84+
85+
// Get the semantic model for our newly added syntax tree
86+
var semanticModel = compilation.GetSemanticModel(syntaxTree);
87+
88+
// Find the class syntax node in the syntax tree
89+
var classNode = syntaxTree.GetRoot().DescendantNodes()
90+
.OfType<ClassDeclarationSyntax>()
91+
.FirstOrDefault();
92+
93+
if (classNode == null) return null;
94+
95+
// Retrieve the ITypeSymbol from the semantic model
96+
var classSymbol = semanticModel.GetDeclaredSymbol(classNode) as ITypeSymbol;
97+
return classSymbol;
98+
}
99+
100+
public static AttributeSyntax GetConverter(string propertyType)
101+
{
102+
// 1. Create the attribute: [JsonConverter(typeof(StringEnumJsonConverter<T>))]
103+
var converterAttribute =
104+
Attribute(
105+
IdentifierName("System.Text.Json.Serialization.JsonConverterAttribute")
106+
)
107+
.WithArgumentList(
108+
AttributeArgumentList(
109+
SingletonSeparatedList(
110+
AttributeArgument(
111+
TypeOfExpression(
112+
GenericName(
113+
Identifier("System.Text.Json.Serialization.JsonStringEnumConverter")
114+
)
115+
.WithTypeArgumentList(
116+
TypeArgumentList(
117+
SingletonSeparatedList<TypeSyntax>(
118+
IdentifierName(propertyType)
119+
)
120+
)
121+
)
122+
)
123+
)
124+
)
125+
)
126+
);
127+
return converterAttribute;
128+
129+
}
130+
}

src/libs/CSharpToJsonSchema.Generators/Conversion/ToModels.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using System.ComponentModel;
2-
using H.Generators;
2+
using CSharpToJsonSchema.Generators.Models;
33
using Microsoft.CodeAnalysis;
44

5-
namespace CSharpToJsonSchema.Generators.Core.Conversion;
5+
namespace CSharpToJsonSchema.Generators.Conversion;
66

77
public static class ToModels
88
{
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace CSharpToJsonSchema.Generators.JsonGen.Base;
4+
5+
internal abstract class AbstractSyntaxHelper : ISyntaxHelper
6+
{
7+
public abstract bool IsCaseSensitive { get; }
8+
9+
public abstract bool IsValidIdentifier(string name);
10+
11+
public abstract SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode name);
12+
13+
public abstract bool IsAnyNamespaceBlock(SyntaxNode node);
14+
15+
public abstract bool IsAttribute(SyntaxNode node);
16+
public abstract SyntaxNode GetNameOfAttribute(SyntaxNode node);
17+
18+
public abstract bool IsAttributeList(SyntaxNode node);
19+
public abstract SeparatedSyntaxList<SyntaxNode> GetAttributesOfAttributeList(SyntaxNode node);
20+
public abstract void AddAttributeTargets(SyntaxNode node, ref ValueListBuilder<SyntaxNode> targets);
21+
22+
public abstract bool IsLambdaExpression(SyntaxNode node);
23+
24+
public abstract void AddAliases(SyntaxNode node, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global);
25+
public abstract void AddAliases(CompilationOptions options, ref ValueListBuilder<(string aliasName, string symbolName)> aliases);
26+
27+
public abstract bool ContainsGlobalAliases(SyntaxNode root);
28+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
using System.Diagnostics;
2+
using CSharpToJsonSchema.Generators.JsonGen.Helpers;
3+
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.CSharp;
5+
using Microsoft.CodeAnalysis.CSharp.Syntax;
6+
7+
namespace CSharpToJsonSchema.Generators.JsonGen.Base;
8+
9+
internal sealed class CSharpSyntaxHelper : AbstractSyntaxHelper
10+
{
11+
public static readonly ISyntaxHelper Instance = new CSharpSyntaxHelper();
12+
13+
private CSharpSyntaxHelper()
14+
{
15+
}
16+
17+
public override bool IsCaseSensitive
18+
=> true;
19+
20+
public override bool IsValidIdentifier(string name)
21+
=> SyntaxFacts.IsValidIdentifier(name);
22+
23+
public override bool IsAnyNamespaceBlock(SyntaxNode node)
24+
=> node is BaseNamespaceDeclarationSyntax;
25+
26+
public override bool IsAttribute(SyntaxNode node)
27+
=> node is AttributeSyntax;
28+
29+
public override SyntaxNode GetNameOfAttribute(SyntaxNode node)
30+
=> ((AttributeSyntax)node).Name;
31+
32+
public override bool IsAttributeList(SyntaxNode node)
33+
=> node is AttributeListSyntax;
34+
35+
public override void AddAttributeTargets(SyntaxNode node, ref ValueListBuilder<SyntaxNode> targets)
36+
{
37+
var attributeList = (AttributeListSyntax)node;
38+
var container = attributeList.Parent;
39+
Debug.Assert(container != null);
40+
41+
// For fields/events, the attribute applies to all the variables declared.
42+
if (container is FieldDeclarationSyntax field)
43+
{
44+
foreach (var variable in field.Declaration.Variables)
45+
targets.Append(variable);
46+
}
47+
else if (container is EventFieldDeclarationSyntax ev)
48+
{
49+
foreach (var variable in ev.Declaration.Variables)
50+
targets.Append(variable);
51+
}
52+
else
53+
{
54+
targets.Append(container);
55+
}
56+
}
57+
58+
public override SeparatedSyntaxList<SyntaxNode> GetAttributesOfAttributeList(SyntaxNode node)
59+
=> ((AttributeListSyntax)node).Attributes;
60+
61+
public override bool IsLambdaExpression(SyntaxNode node)
62+
=> node is LambdaExpressionSyntax;
63+
64+
public override SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode node)
65+
=> RoslynExtensions2.GetUnqualifiedName(((NameSyntax)node).GetUnqualifiedName()).Identifier;
66+
67+
public override void AddAliases(SyntaxNode node, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global)
68+
{
69+
if (node is CompilationUnitSyntax compilationUnit)
70+
{
71+
AddAliases(compilationUnit.Usings, ref aliases, global);
72+
}
73+
else if (node is BaseNamespaceDeclarationSyntax namespaceDeclaration)
74+
{
75+
AddAliases(namespaceDeclaration.Usings, ref aliases, global);
76+
}
77+
else
78+
{
79+
Debug.Fail("This should not be reachable. Caller already checked we had a compilation unit or namespace.");
80+
}
81+
}
82+
83+
private static void AddAliases(SyntaxList<UsingDirectiveSyntax> usings, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global)
84+
{
85+
foreach (var usingDirective in usings)
86+
{
87+
if (usingDirective.Alias is null)
88+
continue;
89+
90+
if (global != usingDirective.GlobalKeyword.Kind() is SyntaxKind.GlobalKeyword)
91+
continue;
92+
93+
// We only care about aliases from one name to another name. e.g. `using X = A.B.C;` That's because
94+
// the caller is only interested in finding a fully-qualified-metadata-name to an attribute.
95+
if (usingDirective.Name is null)
96+
continue;
97+
98+
var aliasName = usingDirective.Alias.Name.Identifier.ValueText;
99+
var symbolName = usingDirective.Name.GetUnqualifiedName().Identifier.ValueText;
100+
aliases.Append((aliasName, symbolName));
101+
}
102+
}
103+
104+
public override void AddAliases(CompilationOptions compilation, ref ValueListBuilder<(string aliasName, string symbolName)> aliases)
105+
{
106+
// C# doesn't have global aliases at the compilation level.
107+
return;
108+
}
109+
110+
public override bool ContainsGlobalAliases(SyntaxNode root)
111+
{
112+
// Global usings can only exist at the compilation-unit level, so no need to dive any deeper than that.
113+
var compilationUnit = (CompilationUnitSyntax)root;
114+
115+
foreach (var directive in compilationUnit.Usings)
116+
{
117+
if (directive.GlobalKeyword.IsKind(SyntaxKind.GlobalKeyword) &&
118+
directive.Alias != null)
119+
{
120+
return true;
121+
}
122+
}
123+
124+
return false;
125+
}
126+
}

0 commit comments

Comments
 (0)