1
+ using System . Collections . Immutable ;
2
+ using System . Text ;
3
+ using Fluid ;
4
+ using Microsoft . CodeAnalysis ;
5
+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
6
+ using Microsoft . CodeAnalysis . Text ;
7
+
8
+ namespace Jint . SourceGenerator
9
+ {
10
+ [ Generator ]
11
+ public class ObjectGenerator : IIncrementalGenerator
12
+ {
13
+ private IFluidTemplate _objectTemplate = null ! ;
14
+
15
+ private const string JsObjectAttribute = "Jint.JsObjectAttribute" ;
16
+ private const string JsFunctionAttribute = "Jint.JsFunctionAttribute" ;
17
+
18
+ public void Initialize ( IncrementalGeneratorInitializationContext context )
19
+ {
20
+ using var stream = new StreamReader ( typeof ( ObjectGenerator ) . Assembly . GetManifestResourceStream ( typeof ( ObjectGenerator ) , "Templates.JsObject.liquid" ) ! ) ;
21
+ var template = stream . ReadToEnd ( ) ;
22
+
23
+ var parser = new FluidParser ( ) ;
24
+ _objectTemplate = parser . Parse ( template ) ;
25
+
26
+ context . RegisterPostInitializationOutput ( ctx => ctx . AddSource (
27
+ "Attributes.g.cs" , SourceText . From ( SourceGenerationHelper . Attributes , Encoding . UTF8 ) ) ) ;
28
+
29
+ IncrementalValuesProvider < ClassDeclarationSyntax > classDeclarations = context . SyntaxProvider
30
+ . CreateSyntaxProvider (
31
+ predicate : static ( s , _ ) => IsSyntaxTargetForGeneration ( s ) ,
32
+ transform : static ( ctx , _ ) => GetSemanticTargetForGeneration ( ctx ) )
33
+ . Where ( static m => m is not null ) ! ;
34
+
35
+ IncrementalValueProvider < ( Compilation , ImmutableArray < ClassDeclarationSyntax > ) > compilationAndClasses
36
+ = context . CompilationProvider . Combine ( classDeclarations . Collect ( ) ) ;
37
+
38
+ context . RegisterSourceOutput ( compilationAndClasses ,
39
+ ( spc , source ) => Execute ( source . Item1 , source . Item2 , spc ) ) ;
40
+ }
41
+
42
+ private static bool IsSyntaxTargetForGeneration ( SyntaxNode node )
43
+ => node is ClassDeclarationSyntax { AttributeLists . Count : > 0 } ;
44
+
45
+ private static ClassDeclarationSyntax ? GetSemanticTargetForGeneration ( GeneratorSyntaxContext context )
46
+ {
47
+ var classDeclarationSyntax = ( ClassDeclarationSyntax ) context . Node ;
48
+
49
+ // loop through all the attributes on the method
50
+ foreach ( AttributeListSyntax attributeListSyntax in classDeclarationSyntax . AttributeLists )
51
+ {
52
+ foreach ( AttributeSyntax attributeSyntax in attributeListSyntax . Attributes )
53
+ {
54
+ var symbolInfo = context . SemanticModel . GetSymbolInfo ( attributeSyntax ) ;
55
+ IMethodSymbol symbol ;
56
+ if ( symbolInfo . Symbol is IMethodSymbol methodSymbol )
57
+ {
58
+ symbol = methodSymbol ;
59
+ }
60
+ else if ( symbolInfo . CandidateSymbols . Length > 0 && symbolInfo . CandidateSymbols [ 0 ] is IMethodSymbol fromCandidate )
61
+ {
62
+ symbol = fromCandidate ;
63
+ }
64
+ else
65
+ {
66
+ // weird, we couldn't get the symbol, ignore it
67
+ continue ;
68
+ }
69
+
70
+ INamedTypeSymbol attributeContainingTypeSymbol = symbol . ContainingType ;
71
+ string fullName = attributeContainingTypeSymbol . ToDisplayString ( ) ;
72
+
73
+ // Is the attribute the [EnumExtensions] attribute?
74
+ if ( fullName == JsObjectAttribute )
75
+ {
76
+ // return the enum
77
+ return classDeclarationSyntax ;
78
+ }
79
+ }
80
+ }
81
+
82
+ // we didn't find the attribute we were looking for
83
+ return null ;
84
+ }
85
+
86
+ private void Execute ( Compilation compilation , ImmutableArray < ClassDeclarationSyntax > classes , SourceProductionContext context )
87
+ {
88
+ if ( classes . IsDefaultOrEmpty )
89
+ {
90
+ // nothing to do yet
91
+ return ;
92
+ }
93
+
94
+ var distinctEnums = classes . Distinct ( ) ;
95
+ var toGenerate = GetTypesToGenerate ( compilation , distinctEnums , context . CancellationToken ) ;
96
+
97
+ var templateOptions = new TemplateOptions { MemberAccessStrategy = UnsafeMemberAccessStrategy . Instance } ;
98
+ foreach ( var target in toGenerate )
99
+ {
100
+ // generate the source code and add it to the output
101
+ var templateContext = new TemplateContext ( target , templateOptions ) ;
102
+ var result = _objectTemplate . Render ( templateContext ) ;
103
+ context . AddSource ( target . Name + ".g.cs" , SourceText . From ( result , Encoding . UTF8 ) ) ;
104
+ }
105
+ }
106
+
107
+ private static List < ObjectDefinition > GetTypesToGenerate ( Compilation compilation , IEnumerable < ClassDeclarationSyntax > classes , CancellationToken ct )
108
+ {
109
+ var classesToGenerate = new List < ObjectDefinition > ( ) ;
110
+ INamedTypeSymbol ? enumAttribute = compilation . GetTypeByMetadataName ( JsObjectAttribute ) ;
111
+ if ( enumAttribute == null )
112
+ {
113
+ // nothing to do if this type isn't available
114
+ return classesToGenerate ;
115
+ }
116
+
117
+ foreach ( var classDeclarationSyntax in classes )
118
+ {
119
+ // stop if we're asked to
120
+ ct . ThrowIfCancellationRequested ( ) ;
121
+
122
+ SemanticModel semanticModel = compilation . GetSemanticModel ( classDeclarationSyntax . SyntaxTree ) ;
123
+ if ( semanticModel . GetDeclaredSymbol ( classDeclarationSyntax ) is not INamedTypeSymbol classSymbol )
124
+ {
125
+ // something went wrong
126
+ continue ;
127
+ }
128
+
129
+ var allMEmbers = classSymbol . GetMembers ( ) ;
130
+ var functions = new List < FunctionDefinition > ( ) ;
131
+ var properties = new List < PropertyDefinition > ( ) ;
132
+
133
+ foreach ( ISymbol member in allMEmbers )
134
+ {
135
+ if ( member is IMethodSymbol method )
136
+ {
137
+ foreach ( var attribute in method . GetAttributes ( ) )
138
+ {
139
+ if ( attribute . AttributeClass ? . Name == "JsFunctionAttribute" )
140
+ {
141
+ functions . Add ( new FunctionDefinition ( method , attribute ) ) ;
142
+ break ;
143
+ }
144
+ }
145
+ }
146
+
147
+ if ( member is IPropertySymbol property )
148
+ {
149
+ foreach ( var attribute in property . GetAttributes ( ) )
150
+ {
151
+ if ( attribute . AttributeClass ? . Name == "JsPropertyAttribute" )
152
+ {
153
+ properties . Add ( new PropertyDefinition ( property , attribute ) ) ;
154
+ break ;
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ functions . Sort ( ) ;
161
+ classesToGenerate . Add ( new ObjectDefinition ( classDeclarationSyntax , functions , properties ) ) ;
162
+ }
163
+
164
+ return classesToGenerate ;
165
+ }
166
+ }
167
+ }
0 commit comments