├── .gitignore ├── GeneratedCode ├── Controllers.g.cs ├── Hubs.g.cs └── Middleware.g.cs ├── SourceGenExperiments ├── CodeWriter.cs ├── Reflection │ ├── MetadataLoadContext.cs │ ├── RoslynAssembly.cs │ ├── RoslynConstructorInfo.cs │ ├── RoslynCustomAttributeData.cs │ ├── RoslynExtensions.cs │ ├── RoslynFieldInfo.cs │ ├── RoslynMemberInfo.cs │ ├── RoslynMethodInfo.cs │ ├── RoslynParameterInfo.cs │ ├── RoslynPropertyInfo.cs │ ├── RoslynType.cs │ └── SharedUtilities.cs ├── RoslynExtensions.cs ├── SourceGenExperiments.csproj └── SourceGenerator.cs ├── SourceGeneratorPlayground.sln └── SourceGeneratorPlayground ├── Controllers └── HomeController.cs ├── Hubs ├── ChatHub.cs └── MessagesHub.cs ├── Program.cs ├── Properties └── launchSettings.json ├── SourceGeneratorPlayground.csproj ├── appsettings.Development.json └── appsettings.json /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # Tye 66 | .tye/ 67 | 68 | # ASP.NET Scaffolding 69 | ScaffoldingReadMe.txt 70 | 71 | # StyleCop 72 | StyleCopReport.xml 73 | 74 | # Files built by Visual Studio 75 | *_i.c 76 | *_p.c 77 | *_h.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.iobj 82 | *.pch 83 | *.pdb 84 | *.ipdb 85 | *.pgc 86 | *.pgd 87 | *.rsp 88 | *.sbr 89 | *.tlb 90 | *.tli 91 | *.tlh 92 | *.tmp 93 | *.tmp_proj 94 | *_wpftmp.csproj 95 | *.log 96 | *.tlog 97 | *.vspscc 98 | *.vssscc 99 | .builds 100 | *.pidb 101 | *.svclog 102 | *.scc 103 | 104 | # Chutzpah Test files 105 | _Chutzpah* 106 | 107 | # Visual C++ cache files 108 | ipch/ 109 | *.aps 110 | *.ncb 111 | *.opendb 112 | *.opensdf 113 | *.sdf 114 | *.cachefile 115 | *.VC.db 116 | *.VC.VC.opendb 117 | 118 | # Visual Studio profiler 119 | *.psess 120 | *.vsp 121 | *.vspx 122 | *.sap 123 | 124 | # Visual Studio Trace Files 125 | *.e2e 126 | 127 | # TFS 2012 Local Workspace 128 | $tf/ 129 | 130 | # Guidance Automation Toolkit 131 | *.gpState 132 | 133 | # ReSharper is a .NET coding add-in 134 | _ReSharper*/ 135 | *.[Rr]e[Ss]harper 136 | *.DotSettings.user 137 | 138 | # TeamCity is a build add-in 139 | _TeamCity* 140 | 141 | # DotCover is a Code Coverage Tool 142 | *.dotCover 143 | 144 | # AxoCover is a Code Coverage Tool 145 | .axoCover/* 146 | !.axoCover/settings.json 147 | 148 | # Coverlet is a free, cross platform Code Coverage Tool 149 | coverage*.json 150 | coverage*.xml 151 | coverage*.info 152 | 153 | # Visual Studio code coverage results 154 | *.coverage 155 | *.coveragexml 156 | 157 | # NCrunch 158 | _NCrunch_* 159 | .*crunch*.local.xml 160 | nCrunchTemp_* 161 | 162 | # MightyMoose 163 | *.mm.* 164 | AutoTest.Net/ 165 | 166 | # Web workbench (sass) 167 | .sass-cache/ 168 | 169 | # Installshield output folder 170 | [Ee]xpress/ 171 | 172 | # DocProject is a documentation generator add-in 173 | DocProject/buildhelp/ 174 | DocProject/Help/*.HxT 175 | DocProject/Help/*.HxC 176 | DocProject/Help/*.hhc 177 | DocProject/Help/*.hhk 178 | DocProject/Help/*.hhp 179 | DocProject/Help/Html2 180 | DocProject/Help/html 181 | 182 | # Click-Once directory 183 | publish/ 184 | 185 | # Publish Web Output 186 | *.[Pp]ublish.xml 187 | *.azurePubxml 188 | # Note: Comment the next line if you want to checkin your web deploy settings, 189 | # but database connection strings (with potential passwords) will be unencrypted 190 | *.pubxml 191 | *.publishproj 192 | 193 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 194 | # checkin your Azure Web App publish settings, but sensitive information contained 195 | # in these scripts will be unencrypted 196 | PublishScripts/ 197 | 198 | # NuGet Packages 199 | *.nupkg 200 | # NuGet Symbol Packages 201 | *.snupkg 202 | # The packages folder can be ignored because of Package Restore 203 | **/[Pp]ackages/* 204 | # except build/, which is used as an MSBuild target. 205 | !**/[Pp]ackages/build/ 206 | # Uncomment if necessary however generally it will be regenerated when needed 207 | #!**/[Pp]ackages/repositories.config 208 | # NuGet v3's project.json files produces more ignorable files 209 | *.nuget.props 210 | *.nuget.targets 211 | 212 | # Microsoft Azure Build Output 213 | csx/ 214 | *.build.csdef 215 | 216 | # Microsoft Azure Emulator 217 | ecf/ 218 | rcf/ 219 | 220 | # Windows Store app package directories and files 221 | AppPackages/ 222 | BundleArtifacts/ 223 | Package.StoreAssociation.xml 224 | _pkginfo.txt 225 | *.appx 226 | *.appxbundle 227 | *.appxupload 228 | 229 | # Visual Studio cache files 230 | # files ending in .cache can be ignored 231 | *.[Cc]ache 232 | # but keep track of directories ending in .cache 233 | !?*.[Cc]ache/ 234 | 235 | # Others 236 | ClientBin/ 237 | ~$* 238 | *~ 239 | *.dbmdl 240 | *.dbproj.schemaview 241 | *.jfm 242 | *.pfx 243 | *.publishsettings 244 | orleans.codegen.cs 245 | 246 | # Including strong name files can present a security risk 247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 248 | #*.snk 249 | 250 | # Since there are multiple workflows, uncomment next line to ignore bower_components 251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 252 | #bower_components/ 253 | 254 | # RIA/Silverlight projects 255 | Generated_Code/ 256 | 257 | # Backup & report files from converting an old project file 258 | # to a newer Visual Studio version. Backup files are not needed, 259 | # because we have git ;-) 260 | _UpgradeReport_Files/ 261 | Backup*/ 262 | UpgradeLog*.XML 263 | UpgradeLog*.htm 264 | ServiceFabricBackup/ 265 | *.rptproj.bak 266 | 267 | # SQL Server files 268 | *.mdf 269 | *.ldf 270 | *.ndf 271 | 272 | # Business Intelligence projects 273 | *.rdl.data 274 | *.bim.layout 275 | *.bim_*.settings 276 | *.rptproj.rsuser 277 | *- [Bb]ackup.rdl 278 | *- [Bb]ackup ([0-9]).rdl 279 | *- [Bb]ackup ([0-9][0-9]).rdl 280 | 281 | # Microsoft Fakes 282 | FakesAssemblies/ 283 | 284 | # GhostDoc plugin setting file 285 | *.GhostDoc.xml 286 | 287 | # Node.js Tools for Visual Studio 288 | .ntvs_analysis.dat 289 | node_modules/ 290 | 291 | # Visual Studio 6 build log 292 | *.plg 293 | 294 | # Visual Studio 6 workspace options file 295 | *.opt 296 | 297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 298 | *.vbw 299 | 300 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 301 | *.vbp 302 | 303 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 304 | *.dsw 305 | *.dsp 306 | 307 | # Visual Studio 6 technical files 308 | *.ncb 309 | *.aps 310 | 311 | # Visual Studio LightSwitch build output 312 | **/*.HTMLClient/GeneratedArtifacts 313 | **/*.DesktopClient/GeneratedArtifacts 314 | **/*.DesktopClient/ModelManifest.xml 315 | **/*.Server/GeneratedArtifacts 316 | **/*.Server/ModelManifest.xml 317 | _Pvt_Extensions 318 | 319 | # Paket dependency manager 320 | .paket/paket.exe 321 | paket-files/ 322 | 323 | # FAKE - F# Make 324 | .fake/ 325 | 326 | # CodeRush personal settings 327 | .cr/personal 328 | 329 | # Python Tools for Visual Studio (PTVS) 330 | __pycache__/ 331 | *.pyc 332 | 333 | # Cake - Uncomment if you are using it 334 | # tools/** 335 | # !tools/packages.config 336 | 337 | # Tabs Studio 338 | *.tss 339 | 340 | # Telerik's JustMock configuration file 341 | *.jmconfig 342 | 343 | # BizTalk build output 344 | *.btp.cs 345 | *.btm.cs 346 | *.odx.cs 347 | *.xsd.cs 348 | 349 | # OpenCover UI analysis results 350 | OpenCover/ 351 | 352 | # Azure Stream Analytics local run output 353 | ASALocalRun/ 354 | 355 | # MSBuild Binary and Structured Log 356 | *.binlog 357 | 358 | # NVidia Nsight GPU debugger configuration file 359 | *.nvuser 360 | 361 | # MFractors (Xamarin productivity tool) working folder 362 | .mfractor/ 363 | 364 | # Local History for Visual Studio 365 | .localhistory/ 366 | 367 | # Visual Studio History (VSHistory) files 368 | .vshistory/ 369 | 370 | # BeatPulse healthcheck temp database 371 | healthchecksdb 372 | 373 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 374 | MigrationBackup/ 375 | 376 | # Ionide (cross platform F# VS Code tools) working folder 377 | .ionide/ 378 | 379 | # Fody - auto-generated XML schema 380 | FodyWeavers.xsd 381 | 382 | # VS Code files for those working on multiple tools 383 | .vscode/* 384 | !.vscode/settings.json 385 | !.vscode/tasks.json 386 | !.vscode/launch.json 387 | !.vscode/extensions.json 388 | *.code-workspace 389 | 390 | # Local History for Visual Studio Code 391 | .history/ 392 | 393 | # Windows Installer files from build outputs 394 | *.cab 395 | *.msi 396 | *.msix 397 | *.msm 398 | *.msp 399 | 400 | # JetBrains Rider 401 | *.sln.iml 402 | 403 | ## 404 | ## Visual studio for Mac 405 | ## 406 | 407 | 408 | # globs 409 | Makefile.in 410 | *.userprefs 411 | *.usertasks 412 | config.make 413 | config.status 414 | aclocal.m4 415 | install-sh 416 | autom4te.cache/ 417 | *.tar.gz 418 | tarballs/ 419 | test-results/ 420 | 421 | # Mac bundle stuff 422 | *.dmg 423 | *.app 424 | 425 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 426 | # General 427 | .DS_Store 428 | .AppleDouble 429 | .LSOverride 430 | 431 | # Icon must end with two \r 432 | Icon 433 | 434 | 435 | # Thumbnails 436 | ._* 437 | 438 | # Files that might appear in the root of a volume 439 | .DocumentRevisions-V100 440 | .fseventsd 441 | .Spotlight-V100 442 | .TemporaryItems 443 | .Trashes 444 | .VolumeIcon.icns 445 | .com.apple.timemachine.donotpresent 446 | 447 | # Directories potentially created on remote AFP share 448 | .AppleDB 449 | .AppleDesktop 450 | Network Trash Folder 451 | Temporary Items 452 | .apdisk 453 | 454 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 455 | # Windows thumbnail cache files 456 | Thumbs.db 457 | ehthumbs.db 458 | ehthumbs_vista.db 459 | 460 | # Dump file 461 | *.stackdump 462 | 463 | # Folder config file 464 | [Dd]esktop.ini 465 | 466 | # Recycle Bin used on file shares 467 | $RECYCLE.BIN/ 468 | 469 | # Windows Installer files 470 | *.cab 471 | *.msi 472 | *.msix 473 | *.msm 474 | *.msp 475 | 476 | # Windows shortcuts 477 | *.lnk 478 | -------------------------------------------------------------------------------- /GeneratedCode/Controllers.g.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | public partial class HomeController 3 | { 4 | public static void BindController(IDictionary> definition) 5 | { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /GeneratedCode/Hubs.g.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNetCore.SignalR 2 | { 3 | // These are types that will be in the framework 4 | public interface IHubDefinition 5 | { 6 | void AddHubMethod(string name, HubInvocationDelegate handler); 7 | void SetHubInitializer(HubInitializerDelegate initializer); 8 | } 9 | public interface IStreamTracker 10 | { 11 | void AddStream(string name, System.Func writeStreamItem, System.Func completeStream); 12 | void RemoveStream(string name); 13 | } 14 | public delegate Task HubInvocationDelegate(Microsoft.AspNetCore.SignalR.Hub hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IStreamTracker streamTracker, Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, System.Threading.CancellationToken cancellationToken); 15 | public delegate void HubInitializerDelegate(Microsoft.AspNetCore.SignalR.Hub hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IHubCallerClients clients); 16 | } 17 | partial class ChatHub 18 | { 19 | static async Task SendThunk(Microsoft.AspNetCore.SignalR.Hub hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IStreamTracker streamTracker, Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, System.Threading.CancellationToken cancellationToken) 20 | { 21 | var invocation = (Microsoft.AspNetCore.SignalR.Protocol.InvocationMessage)message; 22 | var args = invocation.Arguments; 23 | try 24 | { 25 | await ((ChatHub)hub).Send((string)args[0]); 26 | } 27 | catch (Exception ex) when (invocation.InvocationId is not null) 28 | { 29 | await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithError(invocation.InvocationId, "Invoking Send failed")); 30 | return; 31 | } 32 | finally 33 | { 34 | } 35 | 36 | if (invocation.InvocationId is not null) 37 | { 38 | await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithResult(invocation.InvocationId, null)); 39 | } 40 | } 41 | 42 | static async Task LoopThunk(Microsoft.AspNetCore.SignalR.Hub hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IStreamTracker streamTracker, Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, System.Threading.CancellationToken cancellationToken) 43 | { 44 | var invocation = (Microsoft.AspNetCore.SignalR.Protocol.InvocationMessage)message; 45 | var args = invocation.Arguments; 46 | var streamItemMessage = new Microsoft.AspNetCore.SignalR.Protocol.StreamItemMessage(invocation.InvocationId, null); 47 | try 48 | { 49 | await foreach (var item in ((ChatHub)hub).Loop((int)args[0], cancellationToken).WithCancellation(cancellationToken)) 50 | { 51 | streamItemMessage.Item = item; 52 | await connection.WriteAsync(streamItemMessage); 53 | } 54 | } 55 | catch (Exception ex) 56 | { 57 | } 58 | finally 59 | { 60 | } 61 | } 62 | 63 | static async Task UploadDataThunk(Microsoft.AspNetCore.SignalR.Hub hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IStreamTracker streamTracker, Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, System.Threading.CancellationToken cancellationToken) 64 | { 65 | var invocation = (Microsoft.AspNetCore.SignalR.Protocol.StreamInvocationMessage)message; 66 | var channel2 = System.Threading.Channels.Channel.CreateBounded(10); 67 | // Register this channel with the runtime based on this stream id 68 | streamTracker.AddStream(invocation.StreamIds[0], item => channel2.Writer.WriteAsync((byte[])item), (Exception ex) => channel2.Writer.TryComplete(ex)); 69 | var stream2 = channel2.Reader.ReadAllAsync(); 70 | var args = invocation.Arguments; 71 | try 72 | { 73 | await ((ChatHub)hub).UploadData((string)args[0], (System.DateTime)args[1], stream2); 74 | } 75 | catch (Exception ex) when (invocation.InvocationId is not null) 76 | { 77 | await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithError(invocation.InvocationId, "Invoking UploadData failed")); 78 | return; 79 | } 80 | finally 81 | { 82 | channel2.Writer.TryComplete(); 83 | // Unregister this channel with the runtime based on this stream id 84 | streamTracker.RemoveStream(invocation.StreamIds[0]); 85 | 86 | } 87 | 88 | if (invocation.InvocationId is not null) 89 | { 90 | await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithResult(invocation.InvocationId, null)); 91 | } 92 | } 93 | 94 | public static void InitializeHub(Microsoft.AspNetCore.SignalR.Hub hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IHubCallerClients clients) 95 | { 96 | // We need to wrap the original 97 | ((ChatHub)hub).Clients = new ChatHubClientsImpl(clients); 98 | } 99 | 100 | private class IClientImpl : IClient 101 | { 102 | private Microsoft.AspNetCore.SignalR.IClientProxy Proxy { get; } 103 | public IClientImpl(Microsoft.AspNetCore.SignalR.IClientProxy proxy) => Proxy = proxy; 104 | public System.Threading.Tasks.Task Send(string message) => Proxy.SendCoreAsync("Send", new object[] {message}); 105 | } 106 | 107 | private class ChatHubClientsImpl : Microsoft.AspNetCore.SignalR.IHubCallerClients 108 | { 109 | private readonly Microsoft.AspNetCore.SignalR.IHubCallerClients _clients; 110 | public ChatHubClientsImpl(Microsoft.AspNetCore.SignalR.IHubCallerClients clients) => _clients = clients; 111 | 112 | public IClient All => new IClientImpl(_clients.All); 113 | 114 | public IClient Caller => new IClientImpl(_clients.Caller); 115 | 116 | public IClient Others => new IClientImpl(_clients.Others); 117 | 118 | public IClient AllExcept(System.Collections.Generic.IReadOnlyList excludedConnectionIds) => new IClientImpl(_clients.AllExcept(excludedConnectionIds)); 119 | public IClient Client(string connectionId) => new IClientImpl(_clients.Client(connectionId)); 120 | public IClient Clients(System.Collections.Generic.IReadOnlyList connectionIds) => new IClientImpl(_clients.Clients(connectionIds)); 121 | public IClient Group(string groupName) => new IClientImpl(_clients.Group(groupName)); 122 | public IClient Groups(System.Collections.Generic.IReadOnlyList groupNames) => new IClientImpl(_clients.Groups(groupNames)); 123 | public IClient GroupExcept(string groupName, System.Collections.Generic.IReadOnlyList excludedConnectionIds) => new IClientImpl(_clients.GroupExcept(groupName,excludedConnectionIds)); 124 | public IClient User(string userId) => new IClientImpl(_clients.User(userId)); 125 | public IClient Users(System.Collections.Generic.IReadOnlyList userIds) => new IClientImpl(_clients.Users(userIds)); 126 | public IClient OthersInGroup(string groupName) => new IClientImpl(_clients.OthersInGroup(groupName)); 127 | 128 | } 129 | 130 | public static void BindHub(Microsoft.AspNetCore.SignalR.IHubDefinition definition) 131 | { 132 | definition.SetHubInitializer(InitializeHub); 133 | definition.AddHubMethod("Send", SendThunk); 134 | definition.AddHubMethod("Loop", LoopThunk); 135 | definition.AddHubMethod("UploadData", UploadDataThunk); 136 | } 137 | } 138 | namespace SourceGeneratorPlayground 139 | { 140 | public partial class MessagesHub 141 | { 142 | static async Task GroupSendThunk(Microsoft.AspNetCore.SignalR.Hub hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IStreamTracker streamTracker, Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, System.Threading.CancellationToken cancellationToken) 143 | { 144 | var invocation = (Microsoft.AspNetCore.SignalR.Protocol.InvocationMessage)message; 145 | var args = invocation.Arguments; 146 | try 147 | { 148 | await ((SourceGeneratorPlayground.MessagesHub)hub).GroupSend((string)args[0], (string)args[1]); 149 | } 150 | catch (Exception ex) when (invocation.InvocationId is not null) 151 | { 152 | await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithError(invocation.InvocationId, "Invoking GroupSend failed")); 153 | return; 154 | } 155 | finally 156 | { 157 | } 158 | 159 | if (invocation.InvocationId is not null) 160 | { 161 | await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithResult(invocation.InvocationId, null)); 162 | } 163 | } 164 | 165 | static async Task EchoNumbersThunk(Microsoft.AspNetCore.SignalR.Hub hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IStreamTracker streamTracker, Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, System.Threading.CancellationToken cancellationToken) 166 | { 167 | var invocation = (Microsoft.AspNetCore.SignalR.Protocol.StreamInvocationMessage)message; 168 | var channel0 = System.Threading.Channels.Channel.CreateBounded(10); 169 | // Register this channel with the runtime based on this stream id 170 | streamTracker.AddStream(invocation.StreamIds[0], item => channel0.Writer.WriteAsync((int)item), (Exception ex) => channel0.Writer.TryComplete(ex)); 171 | var stream0 = channel0.Reader.ReadAllAsync(); 172 | var args = invocation.Arguments; 173 | var streamItemMessage = new Microsoft.AspNetCore.SignalR.Protocol.StreamItemMessage(invocation.InvocationId, null); 174 | try 175 | { 176 | await foreach (var item in ((SourceGeneratorPlayground.MessagesHub)hub).EchoNumbers(stream0, cancellationToken).WithCancellation(cancellationToken)) 177 | { 178 | streamItemMessage.Item = item; 179 | await connection.WriteAsync(streamItemMessage); 180 | } 181 | } 182 | catch (Exception ex) 183 | { 184 | } 185 | finally 186 | { 187 | channel0.Writer.TryComplete(); 188 | // Unregister this channel with the runtime based on this stream id 189 | streamTracker.RemoveStream(invocation.StreamIds[0]); 190 | } 191 | } 192 | 193 | public static void BindHub(Microsoft.AspNetCore.SignalR.IHubDefinition definition) 194 | { 195 | definition.AddHubMethod("GroupSend", GroupSendThunk); 196 | definition.AddHubMethod("EchoNumbers", EchoNumbersThunk); 197 | } 198 | } 199 | } -------------------------------------------------------------------------------- /GeneratedCode/Middleware.g.cs: -------------------------------------------------------------------------------- 1 | partial class MyMiddleware 2 | { 3 | public static Microsoft.AspNetCore.Http.RequestDelegate CreateDelegate(Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Http.RequestDelegate next) 4 | { 5 | MyMiddleware m = new MyMiddleware(next); 6 | Task HandleRequest(Microsoft.AspNetCore.Http.HttpContext context) 7 | { 8 | var options = context.RequestServices.GetService>(); 9 | return m.Invoke(context, options); 10 | } 11 | return HandleRequest; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SourceGenExperiments/CodeWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace SourceGenerator 4 | { 5 | class CodeWriter 6 | { 7 | private readonly StringBuilder _codeBuilder = new(); 8 | private int _indent; 9 | 10 | public CodeWriter(StringBuilder stringBuilder) 11 | { 12 | _codeBuilder = stringBuilder; 13 | } 14 | 15 | public void StartBlock() 16 | { 17 | WriteLine("{"); 18 | Indent(); 19 | } 20 | 21 | public void EndBlock() 22 | { 23 | Unindent(); 24 | WriteLine("}"); 25 | } 26 | 27 | public void Indent() 28 | { 29 | _indent++; 30 | } 31 | 32 | public void Unindent() 33 | { 34 | _indent--; 35 | } 36 | 37 | public void WriteLineNoIndent(string value) 38 | { 39 | _codeBuilder.AppendLine(value); 40 | } 41 | 42 | public void WriteNoIndent(string value) 43 | { 44 | _codeBuilder.Append(value); 45 | } 46 | 47 | public void Write(string value) 48 | { 49 | if (_indent > 0) 50 | { 51 | _codeBuilder.Append(new string(' ', _indent * 4)); 52 | } 53 | _codeBuilder.Append(value); 54 | } 55 | 56 | public void WriteLine(string value) 57 | { 58 | if (_indent > 0) 59 | { 60 | _codeBuilder.Append(new string(' ', _indent * 4)); 61 | } 62 | _codeBuilder.AppendLine(value); 63 | } 64 | 65 | public void WriteCommentedLine(string value) 66 | { 67 | _codeBuilder.Append("// "); 68 | WriteLine(value); 69 | } 70 | 71 | public override string ToString() 72 | { 73 | return _codeBuilder.ToString(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/MetadataLoadContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace Roslyn.Reflection 7 | { 8 | public class MetadataLoadContext 9 | { 10 | private readonly Compilation _compilation; 11 | 12 | public MetadataLoadContext(Compilation compilation) 13 | { 14 | _compilation = compilation; 15 | } 16 | 17 | public Assembly Assembly => _compilation.Assembly.AsAssembly(this); 18 | 19 | internal Compilation Compilation => _compilation; 20 | 21 | public Type ResolveType(string fullyQualifiedMetadataName) 22 | { 23 | return _compilation.GetTypeByMetadataName(fullyQualifiedMetadataName)?.AsType(this); 24 | } 25 | 26 | public Type ResolveType() => ResolveType(typeof(T)); 27 | 28 | public Type ResolveType(Type type) 29 | { 30 | if (type is RoslynType) 31 | { 32 | return type; 33 | } 34 | 35 | var resolvedType = _compilation.GetTypeByMetadataName(type.FullName); 36 | 37 | if (resolvedType is not null) 38 | { 39 | return resolvedType.AsType(this); 40 | } 41 | 42 | if (type.IsArray) 43 | { 44 | var typeSymbol = _compilation.GetTypeByMetadataName(type.GetElementType().FullName); 45 | if (typeSymbol is null) 46 | { 47 | return null; 48 | } 49 | 50 | return _compilation.CreateArrayTypeSymbol(typeSymbol).AsType(this); 51 | } 52 | 53 | if (type.IsGenericType) 54 | { 55 | var openGenericTypeSymbol = _compilation.GetTypeByMetadataName(type.GetGenericTypeDefinition().FullName); 56 | if (openGenericTypeSymbol is null) 57 | { 58 | return null; 59 | } 60 | 61 | return openGenericTypeSymbol.AsType(this).MakeGenericType(type.GetGenericArguments()); 62 | } 63 | 64 | return null; 65 | } 66 | 67 | public TMember ResolveMember(TMember memberInfo) where TMember : MemberInfo 68 | { 69 | return memberInfo switch 70 | { 71 | RoslynFieldInfo f => (TMember)(object)f, 72 | RoslynMethodInfo m => (TMember)(object)m, 73 | RoslynPropertyInfo p => (TMember)(object)p, 74 | MethodInfo m => (TMember)(object)ResolveType(m.ReflectedType)?.GetMethod(m.Name, SharedUtilities.ComputeBindingFlags(m), binder: null, types: m.GetParameters().Select(t => t.ParameterType).ToArray(), modifiers: null), 75 | PropertyInfo p => (TMember)(object)ResolveType(p.ReflectedType)?.GetProperty(p.Name, SharedUtilities.ComputeBindingFlags(p), binder: null, returnType: p.PropertyType, types: p.GetIndexParameters().Select(t => t.ParameterType).ToArray(), modifiers: null), 76 | FieldInfo f => (TMember)(object)ResolveType(f.ReflectedType)?.GetField(f.Name, SharedUtilities.ComputeBindingFlags(f)), 77 | _ => null 78 | }; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynAssembly.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace Roslyn.Reflection 7 | { 8 | internal class RoslynAssembly : Assembly 9 | { 10 | private readonly MetadataLoadContext _metadataLoadContext; 11 | 12 | public RoslynAssembly(IAssemblySymbol assembly, MetadataLoadContext metadataLoadContext) 13 | { 14 | Symbol = assembly; 15 | _metadataLoadContext = metadataLoadContext; 16 | } 17 | 18 | public override string FullName => Symbol.Name; 19 | 20 | internal IAssemblySymbol Symbol { get; } 21 | 22 | public override Type[] GetExportedTypes() 23 | { 24 | return GetTypes(); 25 | } 26 | 27 | public override Type[] GetTypes() 28 | { 29 | var types = new List(); 30 | var stack = new Stack(); 31 | stack.Push(Symbol.GlobalNamespace); 32 | while (stack.Count > 0) 33 | { 34 | var current = stack.Pop(); 35 | 36 | foreach (var type in current.GetTypeMembers()) 37 | { 38 | types.Add(type.AsType(_metadataLoadContext)); 39 | } 40 | 41 | foreach (var ns in current.GetNamespaceMembers()) 42 | { 43 | stack.Push(ns); 44 | } 45 | } 46 | return types.ToArray(); 47 | } 48 | 49 | public override Type GetType(string name) 50 | { 51 | return Symbol.GetTypeByMetadataName(name).AsType(_metadataLoadContext); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynConstructorInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Reflection; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace Roslyn.Reflection 8 | { 9 | internal class RoslynConstructorInfo : ConstructorInfo 10 | { 11 | private readonly IMethodSymbol _ctor; 12 | private readonly MetadataLoadContext _metadataLoadContext; 13 | 14 | public RoslynConstructorInfo(IMethodSymbol ctor, MetadataLoadContext metadataLoadContext) 15 | { 16 | _ctor = ctor; 17 | _metadataLoadContext = metadataLoadContext; 18 | } 19 | 20 | public override Type DeclaringType => _ctor.ContainingType.AsType(_metadataLoadContext); 21 | 22 | public override MethodAttributes Attributes => throw new NotImplementedException(); 23 | 24 | public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(); 25 | 26 | public override string Name => _ctor.Name; 27 | 28 | public override Type ReflectedType => throw new NotImplementedException(); 29 | 30 | public override bool IsGenericMethod => _ctor.IsGenericMethod; 31 | 32 | public override Type[] GetGenericArguments() 33 | { 34 | var typeArguments = new List(); 35 | foreach (var t in _ctor.TypeArguments) 36 | { 37 | typeArguments.Add(t.AsType(_metadataLoadContext)); 38 | } 39 | return typeArguments.ToArray(); 40 | } 41 | 42 | public override IList GetCustomAttributesData() 43 | { 44 | return SharedUtilities.GetCustomAttributesData(_ctor, _metadataLoadContext); 45 | } 46 | 47 | public override object[] GetCustomAttributes(bool inherit) 48 | { 49 | throw new NotSupportedException(); 50 | } 51 | 52 | public override object[] GetCustomAttributes(Type attributeType, bool inherit) 53 | { 54 | throw new NotSupportedException(); 55 | } 56 | 57 | public override MethodImplAttributes GetMethodImplementationFlags() 58 | { 59 | throw new NotImplementedException(); 60 | } 61 | 62 | public override ParameterInfo[] GetParameters() 63 | { 64 | var parameters = new List(); 65 | foreach (var p in _ctor.Parameters) 66 | { 67 | parameters.Add(new RoslynParameterInfo(p, _metadataLoadContext)); 68 | } 69 | return parameters.ToArray(); 70 | } 71 | 72 | public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 73 | { 74 | throw new NotSupportedException(); 75 | } 76 | 77 | public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 78 | { 79 | throw new NotSupportedException(); 80 | } 81 | 82 | public override bool IsDefined(Type attributeType, bool inherit) 83 | { 84 | throw new NotImplementedException(); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynCustomAttributeData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace Roslyn.Reflection 8 | { 9 | internal class RoslynCustomAttributeData : CustomAttributeData 10 | { 11 | public RoslynCustomAttributeData(AttributeData a, MetadataLoadContext metadataLoadContext) 12 | { 13 | if (a.AttributeConstructor is null) 14 | { 15 | throw new InvalidOperationException(); 16 | } 17 | 18 | var namedArguments = new List(); 19 | foreach (var na in a.NamedArguments) 20 | { 21 | var member = a.AttributeClass.BaseTypes().SelectMany(t => t.GetMembers(na.Key)).First(); 22 | 23 | MemberInfo memberInfo = member switch 24 | { 25 | IPropertySymbol property => property.AsPropertyInfo(metadataLoadContext), 26 | IFieldSymbol field => field.AsFieldInfo(metadataLoadContext), 27 | IMethodSymbol ctor when ctor.MethodKind == MethodKind.Constructor => new RoslynConstructorInfo(ctor, metadataLoadContext), 28 | IMethodSymbol method => method.AsMethodInfo(metadataLoadContext), 29 | ITypeSymbol typeSymbol => typeSymbol.AsType(metadataLoadContext), 30 | _ => new RoslynMemberInfo(member, metadataLoadContext) 31 | }; 32 | namedArguments.Add(new CustomAttributeNamedArgument(memberInfo, na.Value.Value)); 33 | } 34 | 35 | var constructorArguments = new List(); 36 | foreach (var ca in a.ConstructorArguments) 37 | { 38 | if (ca.Kind == TypedConstantKind.Error) 39 | { 40 | continue; 41 | } 42 | 43 | object value = ca.Kind == TypedConstantKind.Array ? ca.Values : ca.Value; 44 | constructorArguments.Add(new CustomAttributeTypedArgument(ca.Type.AsType(metadataLoadContext), value)); 45 | } 46 | Constructor = new RoslynConstructorInfo(a.AttributeConstructor, metadataLoadContext); 47 | NamedArguments = namedArguments; 48 | ConstructorArguments = constructorArguments; 49 | } 50 | 51 | public override ConstructorInfo Constructor { get; } 52 | 53 | public override IList NamedArguments { get; } 54 | 55 | public override IList ConstructorArguments { get; } 56 | } 57 | } -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace Roslyn.Reflection 7 | { 8 | public static class RoslynExtensions 9 | { 10 | public static Assembly AsAssembly(this IAssemblySymbol assemblySymbol, MetadataLoadContext metadataLoadContext) => assemblySymbol == null ? null : new RoslynAssembly(assemblySymbol, metadataLoadContext); 11 | 12 | public static Type AsType(this ITypeSymbol typeSymbol, MetadataLoadContext metadataLoadContext) => typeSymbol == null ? null : new RoslynType(typeSymbol, metadataLoadContext); 13 | 14 | public static ParameterInfo AsParameterInfo(this IParameterSymbol parameterSymbol, MetadataLoadContext metadataLoadContext) => parameterSymbol == null ? null : new RoslynParameterInfo(parameterSymbol, metadataLoadContext); 15 | 16 | public static MethodInfo AsMethodInfo(this IMethodSymbol methodSymbol, MetadataLoadContext metadataLoadContext) => methodSymbol == null ? null : new RoslynMethodInfo(methodSymbol, metadataLoadContext); 17 | 18 | public static PropertyInfo AsPropertyInfo(this IPropertySymbol propertySymbol, MetadataLoadContext metadataLoadContext) => propertySymbol == null ? null : new RoslynPropertyInfo(propertySymbol, metadataLoadContext); 19 | 20 | public static FieldInfo AsFieldInfo(this IFieldSymbol fieldSymbol, MetadataLoadContext metadataLoadContext) => fieldSymbol == null ? null : new RoslynFieldInfo(fieldSymbol, metadataLoadContext); 21 | 22 | public static IMethodSymbol GetMethodSymbol(this MethodInfo methodInfo) => (methodInfo as RoslynMethodInfo)?.MethodSymbol; 23 | 24 | public static IPropertySymbol GetPropertySymbol(this PropertyInfo property) => (property as RoslynPropertyInfo)?.PropertySymbol; 25 | public static IFieldSymbol GetFieldSymbol(this FieldInfo field) => (field as RoslynFieldInfo)?.FieldSymbol; 26 | 27 | public static IParameterSymbol GetParameterSymbol(this ParameterInfo parameterInfo) => (parameterInfo as RoslynParameterInfo)?.ParameterSymbol; 28 | 29 | public static ITypeSymbol GetTypeSymbol(this Type type) => (type as RoslynType)?.TypeSymbol; 30 | 31 | public static IEnumerable BaseTypes(this ITypeSymbol typeSymbol) 32 | { 33 | var t = typeSymbol; 34 | while (t != null) 35 | { 36 | yield return t; 37 | t = t.BaseType; 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynFieldInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Reflection; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace Roslyn.Reflection 8 | { 9 | internal class RoslynFieldInfo : FieldInfo 10 | { 11 | private readonly IFieldSymbol _field; 12 | private readonly MetadataLoadContext _metadataLoadContext; 13 | private FieldAttributes? _attributes; 14 | 15 | public RoslynFieldInfo(IFieldSymbol parameter, MetadataLoadContext metadataLoadContext) 16 | { 17 | _field = parameter; 18 | _metadataLoadContext = metadataLoadContext; 19 | } 20 | 21 | public IFieldSymbol FieldSymbol => _field; 22 | 23 | public override FieldAttributes Attributes 24 | { 25 | get 26 | { 27 | if (!_attributes.HasValue) 28 | { 29 | _attributes = default(FieldAttributes); 30 | 31 | if (_field.IsStatic) 32 | { 33 | _attributes |= FieldAttributes.Static; 34 | } 35 | 36 | if (_field.IsReadOnly) 37 | { 38 | _attributes |= FieldAttributes.InitOnly; 39 | } 40 | 41 | switch (_field.DeclaredAccessibility) 42 | { 43 | case Accessibility.Public: 44 | _attributes |= FieldAttributes.Public; 45 | break; 46 | case Accessibility.Private: 47 | _attributes |= FieldAttributes.Private; 48 | break; 49 | case Accessibility.Protected: 50 | _attributes |= FieldAttributes.Family; 51 | break; 52 | } 53 | } 54 | 55 | return _attributes.Value; 56 | } 57 | } 58 | 59 | public override RuntimeFieldHandle FieldHandle => throw new NotSupportedException(); 60 | 61 | public override Type FieldType => _field.Type.AsType(_metadataLoadContext); 62 | 63 | public override Type DeclaringType => _field.ContainingType.AsType(_metadataLoadContext); 64 | 65 | public override string Name => _field.Name; 66 | 67 | public override Type ReflectedType => throw new NotImplementedException(); 68 | 69 | public override object[] GetCustomAttributes(bool inherit) 70 | { 71 | throw new NotSupportedException(); 72 | } 73 | 74 | public override object[] GetCustomAttributes(Type attributeType, bool inherit) 75 | { 76 | throw new NotSupportedException(); 77 | } 78 | 79 | public override object GetValue(object obj) 80 | { 81 | throw new NotSupportedException(); 82 | } 83 | 84 | public override IList GetCustomAttributesData() 85 | { 86 | return SharedUtilities.GetCustomAttributesData(_field, _metadataLoadContext); 87 | } 88 | 89 | public override bool IsDefined(Type attributeType, bool inherit) 90 | { 91 | throw new NotSupportedException(); 92 | } 93 | 94 | public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture) 95 | { 96 | throw new NotSupportedException(); 97 | } 98 | 99 | public override string ToString() => _field.ToString(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynMemberInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace Roslyn.Reflection 7 | { 8 | internal class RoslynMemberInfo : MemberInfo 9 | { 10 | private readonly ISymbol _member; 11 | private readonly MetadataLoadContext _metadataLoadContext; 12 | 13 | public RoslynMemberInfo(ISymbol member, MetadataLoadContext metadataLoadContext) 14 | { 15 | _member = member; 16 | _metadataLoadContext = metadataLoadContext; 17 | } 18 | 19 | public override Type DeclaringType => _member.ContainingType.AsType(_metadataLoadContext); 20 | 21 | public override MemberTypes MemberType => throw new NotImplementedException(); 22 | 23 | public override string Name => _member.Name; 24 | 25 | public override Type ReflectedType => throw new NotImplementedException(); 26 | 27 | public override IList GetCustomAttributesData() 28 | { 29 | return SharedUtilities.GetCustomAttributesData(_member, _metadataLoadContext); 30 | } 31 | 32 | public override object[] GetCustomAttributes(bool inherit) 33 | { 34 | throw new NotSupportedException(); 35 | } 36 | 37 | public override object[] GetCustomAttributes(Type attributeType, bool inherit) 38 | { 39 | throw new NotSupportedException(); 40 | } 41 | 42 | public override bool IsDefined(Type attributeType, bool inherit) 43 | { 44 | throw new NotSupportedException(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynMethodInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Globalization; 5 | using System.Reflection; 6 | using Microsoft.CodeAnalysis; 7 | 8 | namespace Roslyn.Reflection 9 | { 10 | internal class RoslynMethodInfo : MethodInfo 11 | { 12 | private readonly IMethodSymbol _method; 13 | private readonly MetadataLoadContext _metadataLoadContext; 14 | 15 | public RoslynMethodInfo(IMethodSymbol method, MetadataLoadContext metadataLoadContext) 16 | { 17 | _method = method; 18 | _metadataLoadContext = metadataLoadContext; 19 | 20 | if (method.IsAbstract) 21 | { 22 | Attributes |= MethodAttributes.Abstract | MethodAttributes.Virtual; 23 | } 24 | 25 | if (method.IsStatic) 26 | { 27 | Attributes |= MethodAttributes.Static; 28 | } 29 | 30 | if (method.IsVirtual || method.IsOverride) 31 | { 32 | Attributes |= MethodAttributes.Virtual; 33 | } 34 | 35 | switch (method.DeclaredAccessibility) 36 | { 37 | case Accessibility.Public: 38 | Attributes |= MethodAttributes.Public; 39 | break; 40 | case Accessibility.Private: 41 | Attributes |= MethodAttributes.Private; 42 | break; 43 | case Accessibility.Internal: 44 | Attributes |= MethodAttributes.Assembly; 45 | break; 46 | } 47 | 48 | if (method.MethodKind != MethodKind.Ordinary) 49 | { 50 | Attributes |= MethodAttributes.SpecialName; 51 | } 52 | } 53 | 54 | public override ICustomAttributeProvider ReturnTypeCustomAttributes => throw new NotImplementedException(); 55 | 56 | public override MethodAttributes Attributes { get; } 57 | 58 | public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(); 59 | 60 | public override Type DeclaringType => _method.ContainingType.AsType(_metadataLoadContext); 61 | 62 | public override Type ReturnType => _method.ReturnType.AsType(_metadataLoadContext); 63 | 64 | public override string Name => _method.Name; 65 | 66 | public override bool IsGenericMethod => _method.IsGenericMethod; 67 | 68 | public override Type ReflectedType => throw new NotImplementedException(); 69 | 70 | public IMethodSymbol MethodSymbol => _method; 71 | 72 | public override IList GetCustomAttributesData() 73 | { 74 | return SharedUtilities.GetCustomAttributesData(_method, _metadataLoadContext); 75 | } 76 | 77 | public override MethodInfo GetBaseDefinition() 78 | { 79 | var method = _method; 80 | 81 | // Walk until we find the base definition for this method 82 | while (method.OverriddenMethod is not null) 83 | { 84 | method = method.OverriddenMethod; 85 | } 86 | 87 | if (method.Equals(_method, SymbolEqualityComparer.Default)) 88 | { 89 | return this; 90 | } 91 | 92 | return method.AsMethodInfo(_metadataLoadContext); 93 | } 94 | 95 | public override object[] GetCustomAttributes(bool inherit) 96 | { 97 | throw new NotSupportedException(); 98 | } 99 | 100 | public override object[] GetCustomAttributes(Type attributeType, bool inherit) 101 | { 102 | throw new NotSupportedException(); 103 | } 104 | 105 | public override Type[] GetGenericArguments() 106 | { 107 | List typeArguments = default; 108 | foreach (var t in _method.TypeArguments) 109 | { 110 | typeArguments ??= new(); 111 | typeArguments.Add(t.AsType(_metadataLoadContext)); 112 | } 113 | return typeArguments?.ToArray() ?? Array.Empty(); 114 | } 115 | 116 | public override MethodImplAttributes GetMethodImplementationFlags() 117 | { 118 | throw new NotImplementedException(); 119 | } 120 | 121 | public override ParameterInfo[] GetParameters() 122 | { 123 | List parameters = default; 124 | foreach (var p in _method.Parameters) 125 | { 126 | parameters ??= new(); 127 | parameters.Add(new RoslynParameterInfo(p, _metadataLoadContext)); 128 | } 129 | return parameters?.ToArray() ?? Array.Empty(); 130 | } 131 | 132 | public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 133 | { 134 | throw new NotSupportedException(); 135 | } 136 | 137 | public override bool IsDefined(Type attributeType, bool inherit) 138 | { 139 | throw new NotSupportedException(); 140 | } 141 | 142 | public override string ToString() => _method.ToString(); 143 | } 144 | } -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynParameterInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace Roslyn.Reflection 7 | { 8 | public class RoslynParameterInfo : ParameterInfo 9 | { 10 | private readonly IParameterSymbol _parameter; 11 | private readonly MetadataLoadContext _metadataLoadContext; 12 | 13 | public RoslynParameterInfo(IParameterSymbol parameter, MetadataLoadContext metadataLoadContext) 14 | { 15 | _parameter = parameter; 16 | _metadataLoadContext = metadataLoadContext; 17 | } 18 | 19 | public IParameterSymbol ParameterSymbol => _parameter; 20 | 21 | public override Type ParameterType => _parameter.Type.AsType(_metadataLoadContext); 22 | public override string Name => _parameter.Name; 23 | public override bool HasDefaultValue => _parameter.HasExplicitDefaultValue; 24 | 25 | public override object DefaultValue => HasDefaultValue ? _parameter.ExplicitDefaultValue : null; 26 | 27 | public override int Position => _parameter.Ordinal; 28 | 29 | public override IList GetCustomAttributesData() 30 | { 31 | return SharedUtilities.GetCustomAttributesData(_parameter, _metadataLoadContext); 32 | } 33 | 34 | public override string ToString() => _parameter.ToString(); 35 | } 36 | } -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynPropertyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Reflection; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace Roslyn.Reflection 8 | { 9 | internal class RoslynPropertyInfo : PropertyInfo 10 | { 11 | private readonly IPropertySymbol _property; 12 | private readonly MetadataLoadContext _metadataLoadContext; 13 | 14 | public RoslynPropertyInfo(IPropertySymbol property, MetadataLoadContext metadataLoadContext) 15 | { 16 | _property = property; 17 | _metadataLoadContext = metadataLoadContext; 18 | } 19 | 20 | public IPropertySymbol PropertySymbol => _property; 21 | 22 | public override PropertyAttributes Attributes => throw new NotImplementedException(); 23 | 24 | public override bool CanRead => _property.GetMethod != null; 25 | 26 | public override bool CanWrite => _property.SetMethod != null; 27 | 28 | public override Type PropertyType => _property.Type.AsType(_metadataLoadContext); 29 | 30 | public override Type DeclaringType => _property.ContainingType.AsType(_metadataLoadContext); 31 | 32 | public override string Name => _property.Name; 33 | 34 | public override Type ReflectedType => throw new NotImplementedException(); 35 | 36 | public override MethodInfo[] GetAccessors(bool nonPublic) 37 | { 38 | throw new NotImplementedException(); 39 | } 40 | 41 | public override object[] GetCustomAttributes(bool inherit) 42 | { 43 | throw new NotSupportedException(); 44 | } 45 | 46 | public override object[] GetCustomAttributes(Type attributeType, bool inherit) 47 | { 48 | throw new NotSupportedException(); 49 | } 50 | 51 | public override MethodInfo GetGetMethod(bool nonPublic) 52 | { 53 | return _property.GetMethod.AsMethodInfo(_metadataLoadContext); 54 | } 55 | 56 | public override ParameterInfo[] GetIndexParameters() 57 | { 58 | List parameters = default; 59 | foreach (var p in _property.Parameters) 60 | { 61 | parameters ??= new(); 62 | parameters.Add(new RoslynParameterInfo(p, _metadataLoadContext)); 63 | } 64 | return parameters?.ToArray() ?? Array.Empty(); 65 | } 66 | 67 | public override MethodInfo GetSetMethod(bool nonPublic) 68 | { 69 | return _property.SetMethod.AsMethodInfo(_metadataLoadContext); 70 | } 71 | 72 | public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) 73 | { 74 | throw new NotSupportedException(); 75 | } 76 | 77 | public override bool IsDefined(Type attributeType, bool inherit) 78 | { 79 | throw new NotImplementedException(); 80 | } 81 | 82 | public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) 83 | { 84 | throw new NotSupportedException(); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/RoslynType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Microsoft.CodeAnalysis; 7 | 8 | namespace Roslyn.Reflection 9 | { 10 | internal class RoslynType : Type 11 | { 12 | private readonly ITypeSymbol _typeSymbol; 13 | private readonly MetadataLoadContext _metadataLoadContext; 14 | private readonly bool _isByRef; 15 | private TypeAttributes? _typeAttributes; 16 | 17 | public RoslynType(ITypeSymbol typeSymbol, MetadataLoadContext metadataLoadContext, bool isByRef = false) 18 | { 19 | _typeSymbol = typeSymbol; 20 | _metadataLoadContext = metadataLoadContext; 21 | _isByRef = isByRef; 22 | } 23 | 24 | public override Assembly Assembly => new RoslynAssembly(_typeSymbol.ContainingAssembly, _metadataLoadContext); 25 | 26 | public override string AssemblyQualifiedName => throw new NotImplementedException(); 27 | 28 | public override Type BaseType => _typeSymbol.BaseType.AsType(_metadataLoadContext); 29 | 30 | public override string FullName => Namespace is null ? Name : Namespace + "." + Name; 31 | 32 | public override Guid GUID => Guid.Empty; 33 | 34 | public override Module Module => throw new NotImplementedException(); 35 | 36 | public override string Namespace => _typeSymbol.ContainingNamespace?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted)) is { Length: > 0 } ns ? ns : null; 37 | 38 | public override Type UnderlyingSystemType => this; 39 | 40 | public override string Name => ArrayTypeSymbol is { } ar ? ar.ElementType.MetadataName + "[]" : _typeSymbol.MetadataName; 41 | 42 | public override bool IsGenericType => NamedTypeSymbol?.IsGenericType ?? false; 43 | 44 | private INamedTypeSymbol NamedTypeSymbol => _typeSymbol as INamedTypeSymbol; 45 | 46 | private IArrayTypeSymbol ArrayTypeSymbol => _typeSymbol as IArrayTypeSymbol; 47 | 48 | public override bool IsGenericTypeDefinition => IsGenericType && SymbolEqualityComparer.Default.Equals(NamedTypeSymbol, NamedTypeSymbol.ConstructedFrom); 49 | 50 | public override bool IsGenericParameter => _typeSymbol.TypeKind == TypeKind.TypeParameter; 51 | 52 | public ITypeSymbol TypeSymbol => _typeSymbol; 53 | 54 | public override bool IsEnum => _typeSymbol.TypeKind == TypeKind.Enum; 55 | 56 | public override bool IsConstructedGenericType => NamedTypeSymbol?.IsUnboundGenericType == false; 57 | 58 | public override Type DeclaringType => _typeSymbol.ContainingType?.AsType(_metadataLoadContext); 59 | 60 | public override int GetArrayRank() 61 | { 62 | return ArrayTypeSymbol.Rank; 63 | } 64 | 65 | public override Type[] GetGenericArguments() 66 | { 67 | if (NamedTypeSymbol is null) return Array.Empty(); 68 | 69 | var args = new List(); 70 | foreach (var item in NamedTypeSymbol.TypeArguments) 71 | { 72 | args.Add(item.AsType(_metadataLoadContext)); 73 | } 74 | return args.ToArray(); 75 | } 76 | 77 | public override Type GetGenericTypeDefinition() 78 | { 79 | return NamedTypeSymbol?.ConstructedFrom.AsType(_metadataLoadContext) ?? throw new NotSupportedException(); 80 | } 81 | 82 | public override IList GetCustomAttributesData() 83 | { 84 | return SharedUtilities.GetCustomAttributesData(_typeSymbol, _metadataLoadContext); 85 | } 86 | 87 | public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) 88 | { 89 | if (NamedTypeSymbol is null) 90 | { 91 | return Array.Empty(); 92 | } 93 | 94 | List ctors = default; 95 | foreach (var c in NamedTypeSymbol.Constructors) 96 | { 97 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, c)) 98 | { 99 | continue; 100 | } 101 | 102 | ctors ??= new(); 103 | ctors.Add(new RoslynConstructorInfo(c, _metadataLoadContext)); 104 | } 105 | return ctors?.ToArray() ?? Array.Empty(); 106 | } 107 | 108 | public override Type MakeByRefType() 109 | { 110 | return new RoslynType(_typeSymbol, _metadataLoadContext, isByRef: true); 111 | } 112 | 113 | public override object[] GetCustomAttributes(bool inherit) 114 | { 115 | throw new NotSupportedException(); 116 | } 117 | 118 | public override object[] GetCustomAttributes(Type attributeType, bool inherit) 119 | { 120 | throw new NotSupportedException(); 121 | } 122 | 123 | public override Type MakeArrayType() 124 | { 125 | return _metadataLoadContext.Compilation.CreateArrayTypeSymbol(_typeSymbol).AsType(_metadataLoadContext); 126 | } 127 | 128 | public override Type MakeGenericType(params Type[] typeArguments) 129 | { 130 | if (!IsGenericTypeDefinition) 131 | { 132 | throw new NotSupportedException(); 133 | } 134 | 135 | var typeSymbols = new ITypeSymbol[typeArguments.Length]; 136 | for (int i = 0; i < typeArguments.Length; i++) 137 | { 138 | typeSymbols[i] = _metadataLoadContext.ResolveType(typeArguments[i]).GetTypeSymbol(); 139 | } 140 | 141 | return NamedTypeSymbol.Construct(typeSymbols).AsType(_metadataLoadContext); 142 | } 143 | 144 | public override Type GetElementType() 145 | { 146 | return ArrayTypeSymbol?.ElementType.AsType(_metadataLoadContext); 147 | } 148 | 149 | public override EventInfo GetEvent(string name, BindingFlags bindingAttr) 150 | { 151 | throw new NotImplementedException(); 152 | } 153 | 154 | public override EventInfo[] GetEvents(BindingFlags bindingAttr) 155 | { 156 | throw new NotImplementedException(); 157 | } 158 | 159 | public override FieldInfo GetField(string name, BindingFlags bindingAttr) 160 | { 161 | foreach (var symbol in _typeSymbol.GetMembers()) 162 | { 163 | if (symbol is not IFieldSymbol fieldSymbol) 164 | { 165 | continue; 166 | } 167 | 168 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, symbol)) 169 | { 170 | continue; 171 | } 172 | 173 | return fieldSymbol.AsFieldInfo(_metadataLoadContext); 174 | } 175 | 176 | return null; 177 | } 178 | 179 | public override FieldInfo[] GetFields(BindingFlags bindingAttr) 180 | { 181 | List fields = default; 182 | 183 | foreach (var symbol in _typeSymbol.GetMembers()) 184 | { 185 | if (symbol is not IFieldSymbol fieldSymbol) 186 | { 187 | continue; 188 | } 189 | 190 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, symbol)) 191 | { 192 | continue; 193 | } 194 | 195 | fields ??= new(); 196 | fields.Add(new RoslynFieldInfo(fieldSymbol, _metadataLoadContext)); 197 | } 198 | 199 | return fields?.ToArray() ?? Array.Empty(); 200 | } 201 | 202 | public override Type GetInterface(string name, bool ignoreCase) 203 | { 204 | var comparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; 205 | foreach (var i in _typeSymbol.Interfaces) 206 | { 207 | if (i.Name.Equals(name, comparison)) 208 | { 209 | return i.AsType(_metadataLoadContext); 210 | } 211 | } 212 | return null; 213 | } 214 | 215 | public override Type[] GetInterfaces() 216 | { 217 | List interfaces = default; 218 | foreach (var i in _typeSymbol.Interfaces) 219 | { 220 | interfaces ??= new(); 221 | interfaces.Add(i.AsType(_metadataLoadContext)); 222 | } 223 | return interfaces?.ToArray() ?? Array.Empty(); 224 | } 225 | 226 | public override MemberInfo[] GetMembers(BindingFlags bindingAttr) 227 | { 228 | List members = null; 229 | 230 | foreach (var t in _typeSymbol.BaseTypes()) 231 | { 232 | foreach (var symbol in t.GetMembers()) 233 | { 234 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, symbol)) 235 | { 236 | continue; 237 | } 238 | 239 | MemberInfo member = symbol switch 240 | { 241 | IFieldSymbol f => f.AsFieldInfo(_metadataLoadContext), 242 | IPropertySymbol p => p.AsPropertyInfo(_metadataLoadContext), 243 | IMethodSymbol m => m.AsMethodInfo(_metadataLoadContext), 244 | _ => null 245 | }; 246 | 247 | if (member is null) 248 | { 249 | continue; 250 | } 251 | 252 | members ??= new(); 253 | members.Add(member); 254 | } 255 | } 256 | 257 | // https://github.com/dotnet/runtime/blob/9ec7fc21862f3446c6c6f7dcfff275942e3884d3/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs#L2693-L2694 258 | bindingAttr &= ~BindingFlags.Static; 259 | foreach (var type in GetNestedTypes(bindingAttr)) 260 | { 261 | if (type.IsInterface) 262 | { 263 | continue; 264 | } 265 | 266 | members ??= new(); 267 | members.Add(type); 268 | } 269 | 270 | return members?.ToArray() ?? Array.Empty(); 271 | } 272 | 273 | public override MethodInfo[] GetMethods(BindingFlags bindingAttr) 274 | { 275 | List methods = null; 276 | 277 | foreach (var t in _typeSymbol.BaseTypes()) 278 | { 279 | foreach (var m in t.GetMembers()) 280 | { 281 | if (m is not IMethodSymbol method || method.MethodKind == MethodKind.Constructor) 282 | { 283 | continue; 284 | } 285 | 286 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, method)) 287 | { 288 | continue; 289 | } 290 | 291 | methods ??= new(); 292 | methods.Add(method.AsMethodInfo(_metadataLoadContext)); 293 | } 294 | } 295 | 296 | return methods?.ToArray() ?? Array.Empty(); 297 | } 298 | 299 | public override Type GetNestedType(string name, BindingFlags bindingAttr) 300 | { 301 | foreach (var type in _typeSymbol.GetTypeMembers(name)) 302 | { 303 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, type)) 304 | { 305 | continue; 306 | } 307 | 308 | return type.AsType(_metadataLoadContext); 309 | } 310 | return null; 311 | } 312 | 313 | public override Type[] GetNestedTypes(BindingFlags bindingAttr) 314 | { 315 | List nestedTypes = default; 316 | foreach (var type in _typeSymbol.GetTypeMembers()) 317 | { 318 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, type)) 319 | { 320 | continue; 321 | } 322 | 323 | nestedTypes ??= new(); 324 | nestedTypes.Add(type.AsType(_metadataLoadContext)); 325 | } 326 | return nestedTypes?.ToArray() ?? Array.Empty(); 327 | } 328 | 329 | public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) 330 | { 331 | List properties = default; 332 | foreach (var t in _typeSymbol.BaseTypes()) 333 | { 334 | foreach (var symbol in t.GetMembers()) 335 | { 336 | if (symbol is not IPropertySymbol property) 337 | { 338 | continue; 339 | } 340 | 341 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, symbol)) 342 | { 343 | continue; 344 | } 345 | 346 | properties ??= new(); 347 | properties.Add(new RoslynPropertyInfo(property, _metadataLoadContext)); 348 | } 349 | } 350 | return properties?.ToArray() ?? Array.Empty(); 351 | } 352 | 353 | public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) 354 | { 355 | throw new NotSupportedException(); 356 | } 357 | 358 | public override bool IsDefined(Type attributeType, bool inherit) 359 | { 360 | throw new NotSupportedException(); 361 | } 362 | 363 | protected override TypeAttributes GetAttributeFlagsImpl() 364 | { 365 | if (!_typeAttributes.HasValue) 366 | { 367 | _typeAttributes = default(TypeAttributes); 368 | 369 | if (_typeSymbol.IsAbstract) 370 | { 371 | _typeAttributes |= TypeAttributes.Abstract; 372 | } 373 | 374 | if (_typeSymbol.TypeKind == TypeKind.Interface) 375 | { 376 | _typeAttributes |= TypeAttributes.Interface; 377 | } 378 | 379 | if (_typeSymbol.IsSealed) 380 | { 381 | _typeAttributes |= TypeAttributes.Sealed; 382 | } 383 | 384 | bool isNested = _typeSymbol.ContainingType != null; 385 | 386 | switch (_typeSymbol.DeclaredAccessibility) 387 | { 388 | case Accessibility.NotApplicable: 389 | case Accessibility.Private: 390 | _typeAttributes |= isNested ? TypeAttributes.NestedPrivate : TypeAttributes.NotPublic; 391 | break; 392 | case Accessibility.ProtectedAndInternal: 393 | _typeAttributes |= isNested ? TypeAttributes.NestedFamANDAssem : TypeAttributes.NotPublic; 394 | break; 395 | case Accessibility.Protected: 396 | _typeAttributes |= isNested ? TypeAttributes.NestedFamily : TypeAttributes.NotPublic; 397 | break; 398 | case Accessibility.Internal: 399 | _typeAttributes |= isNested ? TypeAttributes.NestedAssembly : TypeAttributes.NotPublic; 400 | break; 401 | case Accessibility.ProtectedOrInternal: 402 | _typeAttributes |= isNested ? TypeAttributes.NestedFamORAssem : TypeAttributes.NotPublic; 403 | break; 404 | case Accessibility.Public: 405 | _typeAttributes |= isNested ? TypeAttributes.NestedPublic : TypeAttributes.Public; 406 | break; 407 | } 408 | } 409 | 410 | return _typeAttributes.Value; 411 | } 412 | 413 | protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) 414 | { 415 | // TODO: Use callConvention and modifiers 416 | StringComparison comparison = (bindingAttr & BindingFlags.IgnoreCase) == BindingFlags.IgnoreCase 417 | ? StringComparison.OrdinalIgnoreCase 418 | : StringComparison.Ordinal; 419 | 420 | foreach (var m in _typeSymbol.GetMembers()) 421 | { 422 | if (m is not IMethodSymbol method || method.MethodKind != MethodKind.Constructor) 423 | { 424 | // Only methods that are constructors 425 | continue; 426 | } 427 | 428 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, m)) 429 | { 430 | continue; 431 | } 432 | 433 | var valid = true; 434 | 435 | if (types is { Length: > 0 }) 436 | { 437 | var parameterCount = types.Length; 438 | 439 | // Compare parameter types 440 | if (parameterCount != method.Parameters.Length) 441 | { 442 | continue; 443 | } 444 | 445 | for (int i = 0; i < parameterCount; i++) 446 | { 447 | var parameterType = types[i]; 448 | var parameterTypeSymbol = _metadataLoadContext.ResolveType(parameterType)?.GetTypeSymbol(); 449 | 450 | if (parameterTypeSymbol is null) 451 | { 452 | valid = false; 453 | break; 454 | } 455 | 456 | if (!method.Parameters[i].Type.Equals(parameterTypeSymbol, SymbolEqualityComparer.Default)) 457 | { 458 | valid = false; 459 | break; 460 | } 461 | } 462 | } 463 | 464 | if (valid) 465 | { 466 | return new RoslynConstructorInfo(method, _metadataLoadContext); 467 | } 468 | } 469 | 470 | return null; 471 | } 472 | 473 | protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) 474 | { 475 | // TODO: Use callConvention and modifiers 476 | StringComparison comparison = (bindingAttr & BindingFlags.IgnoreCase) == BindingFlags.IgnoreCase 477 | ? StringComparison.OrdinalIgnoreCase 478 | : StringComparison.Ordinal; 479 | 480 | foreach (var m in _typeSymbol.GetMembers()) 481 | { 482 | if (m is not IMethodSymbol method || method.MethodKind == MethodKind.Constructor) 483 | { 484 | // Only methods that are not constructors 485 | continue; 486 | } 487 | 488 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, m)) 489 | { 490 | continue; 491 | } 492 | 493 | if (!method.Name.Equals(name, comparison)) 494 | { 495 | continue; 496 | } 497 | 498 | var valid = true; 499 | 500 | if (types is { Length: > 0 }) 501 | { 502 | var parameterCount = types.Length; 503 | 504 | // Compare parameter types 505 | if (parameterCount != method.Parameters.Length) 506 | { 507 | continue; 508 | } 509 | 510 | for (int i = 0; i < parameterCount; i++) 511 | { 512 | var parameterType = types[i]; 513 | var parameterTypeSymbol = _metadataLoadContext.ResolveType(parameterType)?.GetTypeSymbol(); 514 | 515 | if (parameterTypeSymbol is null) 516 | { 517 | valid = false; 518 | break; 519 | } 520 | 521 | if (!method.Parameters[i].Type.Equals(parameterTypeSymbol, SymbolEqualityComparer.Default)) 522 | { 523 | valid = false; 524 | break; 525 | } 526 | } 527 | } 528 | 529 | if (valid) 530 | { 531 | return method.AsMethodInfo(_metadataLoadContext); 532 | } 533 | } 534 | 535 | return null; 536 | } 537 | 538 | protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) 539 | { 540 | StringComparison comparison = (bindingAttr & BindingFlags.IgnoreCase) == BindingFlags.IgnoreCase 541 | ? StringComparison.OrdinalIgnoreCase 542 | : StringComparison.Ordinal; 543 | 544 | foreach (var symbol in _typeSymbol.GetMembers()) 545 | { 546 | if (symbol is not IPropertySymbol property) 547 | { 548 | continue; 549 | } 550 | 551 | if (!SharedUtilities.MatchBindingFlags(bindingAttr, _typeSymbol, symbol)) 552 | { 553 | continue; 554 | } 555 | 556 | if (!property.Name.Equals(name, comparison)) 557 | { 558 | continue; 559 | } 560 | 561 | var roslynReturnType = _metadataLoadContext.ResolveType(returnType); 562 | 563 | if (roslynReturnType?.Equals(property.Type) == false) 564 | { 565 | continue; 566 | } 567 | 568 | if (types is { Length: > 0 }) 569 | { 570 | var parameterCount = types.Length; 571 | 572 | // Compare parameter types 573 | if (parameterCount != property.Parameters.Length) 574 | { 575 | continue; 576 | } 577 | } 578 | // TODO: Use parameters 579 | 580 | return property.AsPropertyInfo(_metadataLoadContext); 581 | 582 | } 583 | return null; 584 | } 585 | 586 | protected override bool HasElementTypeImpl() 587 | { 588 | return ArrayTypeSymbol is not null; 589 | } 590 | 591 | protected override bool IsArrayImpl() 592 | { 593 | return ArrayTypeSymbol is not null; 594 | } 595 | 596 | protected override bool IsByRefImpl() => _isByRef; 597 | 598 | protected override bool IsCOMObjectImpl() 599 | { 600 | throw new NotImplementedException(); 601 | } 602 | 603 | protected override bool IsPointerImpl() 604 | { 605 | return _typeSymbol.Kind == SymbolKind.PointerType; 606 | } 607 | 608 | protected override bool IsPrimitiveImpl() 609 | { 610 | // Is IsPrimitive 611 | // https://github.com/dotnet/runtime/blob/55e95c80a7d7ec9d7bbbd5ad434604a1dc33e19c/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoType.TypeClassification.cs#L85 612 | 613 | return _typeSymbol.SpecialType switch 614 | { 615 | SpecialType.System_Boolean => true, 616 | SpecialType.System_Char => true, 617 | SpecialType.System_SByte => true, 618 | SpecialType.System_Byte => true, 619 | SpecialType.System_Int16 => true, 620 | SpecialType.System_UInt16 => true, 621 | SpecialType.System_Int32 => true, 622 | SpecialType.System_UInt32 => true, 623 | SpecialType.System_Int64 => true, 624 | SpecialType.System_UInt64 => true, 625 | SpecialType.System_Single => true, 626 | SpecialType.System_Double => true, 627 | SpecialType.System_String => true, 628 | SpecialType.System_IntPtr => true, 629 | SpecialType.System_UIntPtr => true, 630 | _ => false 631 | }; 632 | } 633 | 634 | public override string ToString() 635 | { 636 | return _typeSymbol.ToString(); 637 | } 638 | 639 | public override bool IsAssignableFrom(Type c) 640 | { 641 | if (c is RoslynType rt) 642 | { 643 | return rt._typeSymbol.AllInterfaces.Contains(_typeSymbol, SymbolEqualityComparer.Default) || (rt.NamedTypeSymbol != null && rt.NamedTypeSymbol.BaseTypes().Contains(_typeSymbol, SymbolEqualityComparer.Default)); 644 | } 645 | else if (_metadataLoadContext.ResolveType(c) is RoslynType rtt) 646 | { 647 | return rtt._typeSymbol.AllInterfaces.Contains(_typeSymbol, SymbolEqualityComparer.Default) || (rtt.NamedTypeSymbol != null && rtt.NamedTypeSymbol.BaseTypes().Contains(_typeSymbol, SymbolEqualityComparer.Default)); 648 | } 649 | return false; 650 | } 651 | 652 | public override int GetHashCode() 653 | { 654 | return SymbolEqualityComparer.Default.GetHashCode(_typeSymbol); 655 | } 656 | 657 | public override bool Equals(object o) 658 | { 659 | if (o is RoslynType rt) 660 | { 661 | return _typeSymbol.Equals(rt._typeSymbol, SymbolEqualityComparer.Default); 662 | } 663 | else if (o is Type t && _metadataLoadContext.ResolveType(t) is RoslynType rtt) 664 | { 665 | return _typeSymbol.Equals(rtt._typeSymbol, SymbolEqualityComparer.Default); 666 | } 667 | else if (o is ITypeSymbol ts) 668 | { 669 | return _typeSymbol.Equals(ts, SymbolEqualityComparer.Default); 670 | } 671 | 672 | return false; 673 | } 674 | 675 | public override bool Equals(Type o) 676 | { 677 | if (o is RoslynType rt) 678 | { 679 | return _typeSymbol.Equals(rt._typeSymbol, SymbolEqualityComparer.Default); 680 | } 681 | else if (_metadataLoadContext.ResolveType(o) is RoslynType rtt) 682 | { 683 | return _typeSymbol.Equals(rtt._typeSymbol, SymbolEqualityComparer.Default); 684 | } 685 | return false; 686 | } 687 | } 688 | } 689 | -------------------------------------------------------------------------------- /SourceGenExperiments/Reflection/SharedUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace Roslyn.Reflection 8 | { 9 | internal class SharedUtilities 10 | { 11 | public static IList GetCustomAttributesData(ISymbol symbol, MetadataLoadContext metadataLoadContext) 12 | { 13 | List attributes = default; 14 | foreach (var a in symbol.GetAttributes()) 15 | { 16 | attributes ??= new(); 17 | attributes.Add(new RoslynCustomAttributeData(a, metadataLoadContext)); 18 | } 19 | return (IList)attributes ?? Array.Empty(); 20 | } 21 | 22 | public static bool MatchBindingFlags(BindingFlags bindingFlags, ITypeSymbol thisType, ISymbol symbol) 23 | { 24 | var isPublic = (symbol.DeclaredAccessibility & Accessibility.Public) == Accessibility.Public; 25 | var isNonProtectedInternal = (symbol.DeclaredAccessibility & Accessibility.ProtectedOrInternal) == 0; 26 | var isStatic = symbol.IsStatic; 27 | var isInherited = !SymbolEqualityComparer.Default.Equals(thisType, symbol.ContainingType); 28 | 29 | // TODO: REVIEW precomputing binding flags 30 | // From https://github.com/dotnet/runtime/blob/9ec7fc21862f3446c6c6f7dcfff275942e3884d3/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs#L2058 31 | 32 | //var symbolBindingFlags = ComputeBindingFlags(isPublic, isStatic, isInherited); 33 | 34 | //if (symbol is ITypeSymbol && !isStatic) 35 | //{ 36 | // symbolBindingFlags &= ~BindingFlags.Instance; 37 | //} 38 | 39 | // The below logic is a mishmash of copied logic from the following 40 | 41 | // https://github.com/dotnet/runtime/blob/9ec7fc21862f3446c6c6f7dcfff275942e3884d3/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs#L2261 42 | 43 | // filterFlags ^= BindingFlags.DeclaredOnly; 44 | 45 | // https://github.com/dotnet/runtime/blob/9ec7fc21862f3446c6c6f7dcfff275942e3884d3/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs#L2153 46 | 47 | //if ((filterFlags & symbolBindingFlags) != symbolBindingFlags) 48 | //{ 49 | // return false; 50 | //} 51 | 52 | // Filter by Public & Private 53 | if (isPublic) 54 | { 55 | if ((bindingFlags & BindingFlags.Public) == 0) 56 | { 57 | return false; 58 | } 59 | } 60 | else 61 | { 62 | if ((bindingFlags & BindingFlags.NonPublic) == 0) 63 | { 64 | return false; 65 | } 66 | } 67 | 68 | // Filter by DeclaredOnly 69 | if ((bindingFlags & BindingFlags.DeclaredOnly) != 0 && isInherited) 70 | { 71 | return false; 72 | } 73 | 74 | if (symbol is not ITypeSymbol) 75 | { 76 | if (isStatic) 77 | { 78 | if ((bindingFlags & BindingFlags.FlattenHierarchy) == 0 && isInherited) 79 | { 80 | return false; 81 | } 82 | 83 | if ((bindingFlags & BindingFlags.Static) == 0) 84 | { 85 | return false; 86 | } 87 | } 88 | else 89 | { 90 | if ((bindingFlags & BindingFlags.Instance) == 0) 91 | { 92 | return false; 93 | } 94 | } 95 | } 96 | 97 | // @Asymmetry - Internal, inherited, instance, non -protected, non-virtual, non-abstract members returned 98 | // iff BindingFlags !DeclaredOnly, Instance and Public are present except for fields 99 | if (((bindingFlags & BindingFlags.DeclaredOnly) == 0) && // DeclaredOnly not present 100 | isInherited && // Is inherited Member 101 | 102 | isNonProtectedInternal && // Is non-protected internal member 103 | ((bindingFlags & BindingFlags.NonPublic) != 0) && // BindingFlag.NonPublic present 104 | 105 | (!isStatic) && // Is instance member 106 | ((bindingFlags & BindingFlags.Instance) != 0)) // BindingFlag.Instance present 107 | { 108 | if (symbol is not IMethodSymbol method) 109 | { 110 | return false; 111 | } 112 | 113 | if (!method.IsVirtual && !method.IsAbstract) 114 | { 115 | return false; 116 | } 117 | } 118 | 119 | return true; 120 | } 121 | 122 | public static BindingFlags ComputeBindingFlags(MemberInfo member) 123 | { 124 | if (member is PropertyInfo p) 125 | { 126 | return ComputeBindingFlags(p.GetMethod ?? p.SetMethod); 127 | } 128 | 129 | var (isPublic, isStatic) = member switch 130 | { 131 | FieldInfo f => (f.IsPublic, f.IsStatic), 132 | MethodInfo m => (m.IsPublic, m.IsStatic), 133 | _ => throw new NotSupportedException() 134 | }; 135 | 136 | var isInherited = member.ReflectedType != member.DeclaringType; 137 | 138 | return ComputeBindingFlags(isPublic, isStatic, isInherited); 139 | } 140 | 141 | private static BindingFlags ComputeBindingFlags(bool isPublic, bool isStatic, bool isInherited) 142 | { 143 | BindingFlags bindingFlags = isPublic ? BindingFlags.Public : BindingFlags.NonPublic; 144 | 145 | if (isInherited) 146 | { 147 | // We arrange things so the DeclaredOnly flag means "include inherited members" 148 | bindingFlags |= BindingFlags.DeclaredOnly; 149 | 150 | if (isStatic) 151 | { 152 | bindingFlags |= BindingFlags.Static | BindingFlags.FlattenHierarchy; 153 | } 154 | else 155 | { 156 | bindingFlags |= BindingFlags.Instance; 157 | } 158 | } 159 | else 160 | { 161 | if (isStatic) 162 | { 163 | bindingFlags |= BindingFlags.Static; 164 | } 165 | else 166 | { 167 | bindingFlags |= BindingFlags.Instance; 168 | } 169 | } 170 | 171 | return bindingFlags; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /SourceGenExperiments/RoslynExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http.Headers; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | using Microsoft.CodeAnalysis.CSharp; 8 | 9 | namespace SourceGenExperiments; 10 | 11 | internal static class RoslynExtensions 12 | { 13 | public static bool IsPartial(this ISymbol symbol) 14 | { 15 | foreach (var reference in symbol.DeclaringSyntaxReferences) 16 | { 17 | var syntax = reference.GetSyntax(); 18 | 19 | if (syntax is ClassDeclarationSyntax c && c.Modifiers.Any(SyntaxKind.PartialKeyword)) 20 | { 21 | return true; 22 | } 23 | } 24 | 25 | return false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SourceGenExperiments/SourceGenExperiments.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 11.0 6 | The code generator for trying stuff 7 | David Fowler 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /SourceGenExperiments/SourceGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Microsoft.CodeAnalysis; 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | using Microsoft.CodeAnalysis.Text; 12 | using Roslyn.Reflection; 13 | using SourceGenerator; 14 | 15 | namespace SourceGenExperiments 16 | { 17 | [Generator] 18 | public class SourceGenerator : ISourceGenerator 19 | { 20 | public void Execute(GeneratorExecutionContext context) 21 | { 22 | if (context.SyntaxReceiver is not SyntaxReceiver receiver) 23 | { 24 | return; 25 | } 26 | 27 | var metadataLoadContext = new MetadataLoadContext(context.Compilation); 28 | 29 | DiscoverMiddleware(receiver.MapMiddleware, context, metadataLoadContext); 30 | DiscoverHubs(context, metadataLoadContext); 31 | DiscoverControllers(context, metadataLoadContext); 32 | } 33 | 34 | private void DiscoverMiddleware(List calls, GeneratorExecutionContext context, MetadataLoadContext metadataLoadContext) 35 | { 36 | var appbuilderType = metadataLoadContext.ResolveType("Microsoft.AspNetCore.Builder.IApplicationBuilder"); 37 | 38 | var sb = new StringBuilder(); 39 | var writer = new CodeWriter(sb); 40 | 41 | var middlewareTypes = new List<(Location, Type)>(); 42 | 43 | foreach (var invocation in calls) 44 | { 45 | var semanticModel = context.Compilation.GetSemanticModel(invocation.SyntaxTree); 46 | 47 | var mapMiddlewareMethod = semanticModel.GetSymbolInfo(invocation).Symbol as IMethodSymbol; 48 | 49 | if (mapMiddlewareMethod.IsExtensionMethod && 50 | mapMiddlewareMethod.IsGenericMethod && 51 | appbuilderType.Equals(mapMiddlewareMethod.ReceiverType)) 52 | { 53 | // We only want to generate overloads for calls that have a Delegate parameter 54 | } 55 | else 56 | { 57 | continue; 58 | } 59 | 60 | middlewareTypes.Add((invocation.GetLocation(), mapMiddlewareMethod.TypeArguments[0].AsType(metadataLoadContext))); 61 | } 62 | 63 | foreach (var (l, t) in middlewareTypes) 64 | { 65 | if (!t.GetTypeSymbol().IsPartial()) 66 | { 67 | // TODO: Diagnostic! 68 | continue; 69 | } 70 | 71 | if (t.Namespace is { } ns) 72 | { 73 | writer.WriteLine($"namespace {ns}"); 74 | writer.StartBlock(); 75 | } 76 | 77 | var invokeMethod = t.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public); 78 | 79 | //foreach (var m in t.GetMethods()) 80 | //{ 81 | // if (!m.Name.Equals("Invoke")) 82 | // { 83 | // continue; 84 | // } 85 | // writer.WriteCommentedLine(m.Name); 86 | //} 87 | 88 | if (invokeMethod is null) 89 | { 90 | context.ReportDiagnostic(Diagnostic.Create(Diagnostics.MissingInvokeMethod, l, t.FullName)); 91 | continue; 92 | } 93 | 94 | writer.WriteLine($"partial class {t}"); 95 | writer.StartBlock(); 96 | 97 | writer.WriteLine($"public static Microsoft.AspNetCore.Http.RequestDelegate CreateDelegate({appbuilderType} app, Microsoft.AspNetCore.Http.RequestDelegate next)"); 98 | writer.StartBlock(); 99 | var ctors = t.GetConstructors(); 100 | if (ctors.Length == 0) 101 | { 102 | writer.WriteLine($"{t} m = new {t}();"); 103 | } 104 | else if (ctors.Length == 1) 105 | { 106 | var ctor = ctors[0]; 107 | var ctorParameters = ctor.GetParameters(); 108 | 109 | if (ctorParameters.Length == 1) 110 | { 111 | writer.WriteLine($"{t} m = new {t}(next);"); 112 | } 113 | else 114 | { 115 | // Services? 116 | writer.WriteLine("// Do ActivtorUtilities"); 117 | writer.WriteLine($"{t} m = default;"); 118 | } 119 | } 120 | else 121 | { 122 | writer.WriteLine("// Do ActivtorUtilities"); 123 | writer.WriteLine($"{t} m = default;"); 124 | } 125 | var parameters = invokeMethod.GetParameters(); 126 | 127 | if (parameters.Length == 1) 128 | { 129 | writer.WriteLine("return m.Invoke;"); 130 | } 131 | else 132 | { 133 | writer.WriteLine("Task HandleRequest(Microsoft.AspNetCore.Http.HttpContext context)"); 134 | writer.StartBlock(); 135 | foreach (var p in parameters) 136 | { 137 | if (p.Position == 0) continue; 138 | writer.WriteLine($"var {p.Name} = context.RequestServices.GetService<{p.ParameterType}>();"); 139 | } 140 | writer.Write("return m.Invoke(context"); 141 | foreach (var p in parameters) 142 | { 143 | if (p.Position == 0) continue; 144 | writer.WriteNoIndent(", "); 145 | // Services 146 | writer.WriteNoIndent(p.Name); 147 | } 148 | writer.WriteLineNoIndent(");"); 149 | writer.EndBlock(); 150 | writer.WriteLine("return HandleRequest;"); 151 | } 152 | writer.EndBlock(); 153 | 154 | writer.EndBlock(); 155 | 156 | if (t.Namespace is not null) 157 | { 158 | writer.EndBlock(); 159 | } 160 | } 161 | 162 | context.AddSource("Middleware.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8)); 163 | } 164 | 165 | private static void DiscoverControllers(GeneratorExecutionContext context, MetadataLoadContext metadataLoadContext) 166 | { 167 | var nonControllerAttributeType = metadataLoadContext.ResolveType("Microsoft.AspNetCore.Mvc.NonControllerAttribute"); 168 | var nonActionAttributeType = metadataLoadContext.ResolveType("Microsoft.AspNetCore.Mvc.NonActionAttribute"); 169 | var controllerAttributeType = metadataLoadContext.ResolveType("Microsoft.AspNetCore.Mvc.ControllerAttribute"); 170 | 171 | var sb = new StringBuilder(); 172 | var writer = new CodeWriter(sb); 173 | writer.WriteLine("using Microsoft.AspNetCore.Http;"); 174 | 175 | foreach (var t in metadataLoadContext.Assembly.GetTypes()) 176 | { 177 | if (!IsController(t)) 178 | { 179 | continue; 180 | } 181 | 182 | if (!t.GetTypeSymbol().IsPartial()) 183 | { 184 | // Report diagnostics 185 | continue; 186 | } 187 | 188 | if (t.Namespace is { } ns) 189 | { 190 | writer.WriteLine($"namespace {ns}"); 191 | writer.StartBlock(); 192 | } 193 | 194 | //writer.WriteLine(""); 195 | //writer.WriteLine("delegate "); 196 | //writer.WriteLine(""); 197 | 198 | writer.WriteLine($"public partial class {t.Name}"); 199 | writer.StartBlock(); 200 | 201 | writer.WriteLine(@$"public static void BindController(IDictionary> definition)"); 202 | writer.StartBlock(); 203 | 204 | foreach (var m in t.GetMethods(BindingFlags.Public | BindingFlags.Instance)) 205 | { 206 | if (!IsAction(t, m)) 207 | { 208 | continue; 209 | } 210 | 211 | //writer.StartBlock(); 212 | //writer.EndBlock(); 213 | } 214 | 215 | writer.EndBlock(); 216 | writer.EndBlock(); 217 | 218 | if (t.Namespace is not null) 219 | { 220 | writer.EndBlock(); 221 | } 222 | } 223 | 224 | context.AddSource("Controllers.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8)); 225 | 226 | bool IsController(Type typeInfo) 227 | { 228 | if (!typeInfo.IsClass) 229 | { 230 | return false; 231 | } 232 | 233 | if (typeInfo.IsAbstract) 234 | { 235 | return false; 236 | } 237 | 238 | // We only consider public top-level classes as controllers. IsPublic returns false for nested 239 | // classes, regardless of visibility modifiers 240 | if (!typeInfo.IsPublic) 241 | { 242 | return false; 243 | } 244 | 245 | if (typeInfo.ContainsGenericParameters) 246 | { 247 | return false; 248 | } 249 | 250 | if (typeInfo.CustomAttributes.Any(a => nonControllerAttributeType.IsAssignableFrom(a.AttributeType))) 251 | { 252 | return false; 253 | } 254 | 255 | if (!typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && 256 | !typeInfo.CustomAttributes.Any(a => controllerAttributeType.IsAssignableFrom(a.AttributeType))) 257 | { 258 | return false; 259 | } 260 | 261 | return true; 262 | } 263 | 264 | bool IsAction(Type typeInfo, MethodInfo methodInfo) 265 | { 266 | // The SpecialName bit is set to flag members that are treated in a special way by some compilers 267 | // (such as property accessors and operator overloading methods). 268 | if (methodInfo.IsSpecialName) 269 | { 270 | return false; 271 | } 272 | 273 | if (methodInfo.CustomAttributes.Any(a => nonActionAttributeType.IsAssignableFrom(a.AttributeType))) 274 | { 275 | return false; 276 | } 277 | 278 | // Overridden methods from Object class, e.g. Equals(Object), GetHashCode(), etc., are not valid. 279 | if (methodInfo.GetBaseDefinition().DeclaringType.Equals(typeof(object))) 280 | { 281 | return false; 282 | } 283 | 284 | // Dispose method implemented from IDisposable is not valid 285 | if (IsIDisposableMethod(methodInfo)) 286 | { 287 | return false; 288 | } 289 | 290 | if (methodInfo.IsStatic) 291 | { 292 | return false; 293 | } 294 | 295 | if (methodInfo.IsAbstract) 296 | { 297 | return false; 298 | } 299 | 300 | if (methodInfo.IsConstructor) 301 | { 302 | return false; 303 | } 304 | 305 | if (methodInfo.IsGenericMethod) 306 | { 307 | return false; 308 | } 309 | 310 | return methodInfo.IsPublic; 311 | } 312 | 313 | static bool IsIDisposableMethod(MethodInfo methodInfo) 314 | { 315 | // Ideally we do not want Dispose method to be exposed as an action. However there are some scenarios where a user 316 | // might want to expose a method with name "Dispose" (even though they might not be really disposing resources) 317 | // Example: A controller deriving from MVC's Controller type might wish to have a method with name Dispose, 318 | // in which case they can use the "new" keyword to hide the base controller's declaration. 319 | 320 | // Find where the method was originally declared 321 | var baseMethodInfo = methodInfo.GetBaseDefinition(); 322 | var declaringType = baseMethodInfo.DeclaringType; 323 | 324 | return 325 | (typeof(IDisposable).IsAssignableFrom(declaringType) && 326 | declaringType.GetInterfaceMap(typeof(IDisposable)).TargetMethods[0] == baseMethodInfo); 327 | } 328 | } 329 | 330 | private static void DiscoverHubs(GeneratorExecutionContext context, MetadataLoadContext metadataLoadContext) 331 | { 332 | var hubType = metadataLoadContext.ResolveType("Microsoft.AspNetCore.SignalR.Hub"); 333 | var hubOfTType = metadataLoadContext.ResolveType("Microsoft.AspNetCore.SignalR.Hub`1"); 334 | var hubCallerClientsType = metadataLoadContext.ResolveType("Microsoft.AspNetCore.SignalR.IHubCallerClients`1"); 335 | var hubCallerClientsTypes = hubCallerClientsType.GetInterfaces().Concat(new[] { hubCallerClientsType }).ToArray(); 336 | var asyncEnumerable = metadataLoadContext.ResolveType("System.Collections.Generic.IAsyncEnumerable`1"); 337 | 338 | var sb = new StringBuilder(); 339 | var writer = new CodeWriter(sb); 340 | 341 | var hubTypes = new List(); 342 | 343 | writer.WriteLine("namespace Microsoft.AspNetCore.SignalR"); 344 | writer.StartBlock(); 345 | writer.WriteLine("// These are types that will be in the framework"); 346 | 347 | writer.WriteLine("public interface IHubDefinition"); 348 | writer.StartBlock(); 349 | writer.WriteLine("void AddHubMethod(string name, HubInvocationDelegate handler);"); 350 | writer.WriteLine("void SetHubInitializer(HubInitializerDelegate initializer);"); 351 | writer.EndBlock(); 352 | 353 | writer.WriteLine("public interface IStreamTracker"); 354 | writer.StartBlock(); 355 | writer.WriteLine("void AddStream(string name, System.Func writeStreamItem, System.Func completeStream);"); 356 | writer.WriteLine("void RemoveStream(string name);"); 357 | writer.EndBlock(); 358 | writer.WriteLine($"public delegate Task HubInvocationDelegate({hubType} hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IStreamTracker streamTracker, Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, {typeof(CancellationToken)} cancellationToken);"); 359 | writer.WriteLine($"public delegate void HubInitializerDelegate({hubType} hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IHubCallerClients clients);"); 360 | writer.EndBlock(); 361 | 362 | foreach (var t in metadataLoadContext.Assembly.GetTypes()) 363 | { 364 | if (hubType.IsAssignableFrom(t) && !t.Equals(hubType)) 365 | { 366 | if (!t.GetTypeSymbol().IsPartial()) 367 | { 368 | // Diagnostic! 369 | continue; 370 | } 371 | 372 | hubTypes.Add(t); 373 | } 374 | } 375 | 376 | foreach (var t in hubTypes) 377 | { 378 | var globalNs = t.Namespace is null; 379 | if (!globalNs) 380 | { 381 | writer.WriteLine($"namespace {t.Namespace}"); 382 | writer.StartBlock(); 383 | } 384 | 385 | writer.WriteLine($"{(t.IsPublic ? "public " : "")}partial class {t.Name}"); 386 | writer.StartBlock(); 387 | 388 | var generatedMethods = new List<(string, string)>(); 389 | 390 | var index = 0; 391 | foreach (var m in t.GetMethods(BindingFlags.Public | BindingFlags.Instance)) 392 | { 393 | if (!IsHubMethod(m)) 394 | { 395 | continue; 396 | } 397 | 398 | if (index > 0) 399 | { 400 | writer.WriteLine(""); 401 | } 402 | 403 | var parameters = m.GetParameters(); 404 | var streamingParameters = new Type[parameters.Length]; 405 | var hasStreamingParameters = false; 406 | 407 | foreach (var p in parameters) 408 | { 409 | if (IsAsyncEnumerable(p.ParameterType)) 410 | { 411 | // Streaming parameter 412 | streamingParameters[p.Position] = p.ParameterType.GetGenericArguments()[0]; 413 | hasStreamingParameters = true; 414 | } 415 | } 416 | 417 | var hasStreamingReturn = IsAsyncEnumerable(m.ReturnType); 418 | 419 | var generatedMethod = $"{m.Name}Thunk"; 420 | generatedMethods.Add((m.Name, generatedMethod)); 421 | 422 | writer.WriteLine($"static async Task {generatedMethod}({hubType} hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IStreamTracker streamTracker, Microsoft.AspNetCore.SignalR.Protocol.HubMessage message, {typeof(CancellationToken)} cancellationToken)"); 423 | writer.StartBlock(); 424 | 425 | if (hasStreamingParameters) 426 | { 427 | writer.WriteLine("var invocation = (Microsoft.AspNetCore.SignalR.Protocol.StreamInvocationMessage)message;"); 428 | var streamingIndex = 0; 429 | for (int i = 0; i < streamingParameters.Length; i++) 430 | { 431 | if (streamingParameters[i] is not Type streamType) 432 | { 433 | continue; 434 | } 435 | writer.WriteLine($"var channel{i} = System.Threading.Channels.Channel.CreateBounded<{streamType}>(10);"); 436 | writer.WriteLine("// Register this channel with the runtime based on this stream id"); 437 | writer.WriteLine($"streamTracker.AddStream(invocation.StreamIds[{streamingIndex++}], item => channel{i}.Writer.WriteAsync(({streamType})item), (Exception ex) => channel{i}.Writer.TryComplete(ex));"); 438 | writer.WriteLine($"var stream{i} = channel{i}.Reader.ReadAllAsync();"); 439 | } 440 | } 441 | else 442 | { 443 | writer.WriteLine("var invocation = (Microsoft.AspNetCore.SignalR.Protocol.InvocationMessage)message;"); 444 | } 445 | 446 | if (!hasStreamingReturn) 447 | { 448 | // Hub invocation 449 | writer.WriteLine("var args = invocation.Arguments;"); 450 | 451 | var task = m.ReturnType.Equals(typeof(Task)) || 452 | m.ReturnType.Equals(typeof(ValueTask)); 453 | var taskOfT = m.ReturnType.IsGenericType && 454 | (m.ReturnType.GetGenericTypeDefinition().Equals(typeof(Task<>)) || 455 | m.ReturnType.GetGenericTypeDefinition().Equals(typeof(ValueTask<>))); 456 | 457 | var hasVoidReturn = m.ReturnType.Equals(typeof(void)); 458 | var hasAwait = task || taskOfT; 459 | var hasResult = taskOfT || !(hasVoidReturn || task); 460 | 461 | if (hasResult) 462 | { 463 | var returnType = hasAwait ? m.ReturnType.GetGenericArguments()[0] : m.ReturnType; 464 | writer.WriteLine($"{returnType} result = default;"); 465 | } 466 | 467 | writer.WriteLine("try"); 468 | writer.StartBlock(); 469 | if (taskOfT) 470 | { 471 | writer.Write($"result = await (({t})hub).{m.Name}("); 472 | } 473 | else if (task) 474 | { 475 | writer.Write($"await (({t})hub).{m.Name}("); 476 | } 477 | else if (hasVoidReturn) 478 | { 479 | writer.Write($"(({t})hub).{m.Name}("); 480 | } 481 | else 482 | { 483 | writer.Write($"result = (({t})hub).{m.Name}("); 484 | } 485 | var argIndex = 0; 486 | foreach (var p in parameters) 487 | { 488 | if (p.Position > 0) 489 | { 490 | writer.WriteNoIndent(", "); 491 | } 492 | 493 | if (streamingParameters[p.Position] is Type streamingType) 494 | { 495 | writer.WriteNoIndent($"stream{p.Position}"); 496 | } 497 | else 498 | { 499 | writer.WriteNoIndent($"({p.ParameterType})args[{argIndex}]"); 500 | argIndex++; 501 | } 502 | } 503 | writer.WriteLineNoIndent(");"); 504 | 505 | writer.EndBlock(); 506 | writer.WriteLine("catch (Exception ex) when (invocation.InvocationId is not null)"); 507 | writer.StartBlock(); 508 | writer.WriteLine($@"await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithError(invocation.InvocationId, ""Invoking {m.Name} failed""));"); 509 | writer.WriteLine("return;"); 510 | writer.EndBlock(); 511 | 512 | writer.WriteLine("finally"); 513 | writer.StartBlock(); 514 | if (hasStreamingParameters) 515 | { 516 | var streamingIndex = 0; 517 | for (int i = 0; i < streamingParameters.Length; i++) 518 | { 519 | if (streamingParameters[i] is not Type streamType) 520 | { 521 | continue; 522 | } 523 | writer.WriteLine($"channel{i}.Writer.TryComplete();"); 524 | writer.WriteLine("// Unregister this channel with the runtime based on this stream id"); 525 | writer.WriteLine($"streamTracker.RemoveStream(invocation.StreamIds[{streamingIndex++}]);"); 526 | } 527 | writer.WriteLine(""); 528 | } 529 | writer.EndBlock(); 530 | 531 | writer.WriteLine(""); 532 | writer.WriteLine("if (invocation.InvocationId is not null)"); 533 | writer.StartBlock(); 534 | writer.WriteLine($"await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithResult(invocation.InvocationId, {(hasResult ? "result" : "null")}));"); 535 | writer.EndBlock(); 536 | } 537 | else 538 | { 539 | writer.WriteLine("var args = invocation.Arguments;"); 540 | writer.WriteLine("var streamItemMessage = new Microsoft.AspNetCore.SignalR.Protocol.StreamItemMessage(invocation.InvocationId, null);"); 541 | 542 | writer.WriteLine("try"); 543 | writer.StartBlock(); 544 | 545 | writer.Write($"await foreach (var item in (({t})hub).{m.Name}("); 546 | var argIndex = 0; 547 | foreach (var p in parameters) 548 | { 549 | if (p.Position > 0) 550 | { 551 | writer.WriteNoIndent(", "); 552 | } 553 | 554 | if (streamingParameters[p.Position] is Type streamingType) 555 | { 556 | writer.WriteNoIndent($"stream{p.Position}"); 557 | } 558 | else if (p.ParameterType.Equals(typeof(CancellationToken))) 559 | { 560 | writer.WriteNoIndent("cancellationToken"); 561 | } 562 | else 563 | { 564 | writer.WriteNoIndent($"({p.ParameterType})args[{argIndex}]"); 565 | argIndex++; 566 | } 567 | } 568 | writer.WriteLineNoIndent(").WithCancellation(cancellationToken))"); 569 | 570 | // foreach 571 | writer.StartBlock(); 572 | writer.WriteLine("streamItemMessage.Item = item;"); 573 | writer.WriteLine("await connection.WriteAsync(streamItemMessage);"); 574 | writer.EndBlock(); 575 | 576 | writer.EndBlock(); 577 | writer.WriteLine("catch (Exception ex)"); 578 | writer.StartBlock(); 579 | // writer.WriteLine($@"await connection.WriteAsync(Microsoft.AspNetCore.SignalR.Protocol.CompletionMessage.WithError(invocation.InvocationId, ""Invoking {m.Name} failed""));"); 580 | // writer.WriteLine("return;"); 581 | writer.EndBlock(); 582 | 583 | writer.WriteLine("finally"); 584 | writer.StartBlock(); 585 | if (hasStreamingParameters) 586 | { 587 | var streamingIndex = 0; 588 | for (int i = 0; i < streamingParameters.Length; i++) 589 | { 590 | if (streamingParameters[i] is not Type streamType) 591 | { 592 | continue; 593 | } 594 | writer.WriteLine($"channel{i}.Writer.TryComplete();"); 595 | writer.WriteLine("// Unregister this channel with the runtime based on this stream id"); 596 | writer.WriteLine($"streamTracker.RemoveStream(invocation.StreamIds[{streamingIndex++}]);"); 597 | } 598 | } 599 | writer.EndBlock(); 600 | } 601 | writer.EndBlock(); 602 | 603 | index++; 604 | } 605 | 606 | if (IsHubOfT(t, out var interfaceType)) 607 | { 608 | writer.WriteLine(""); 609 | writer.WriteLine(@$"public static void InitializeHub({hubType} hub, Microsoft.AspNetCore.SignalR.HubConnectionContext connection, Microsoft.AspNetCore.SignalR.IHubCallerClients clients)"); 610 | writer.StartBlock(); 611 | writer.WriteLine("// We need to wrap the original"); 612 | writer.WriteLine($"(({t})hub).Clients = new {t.Name}ClientsImpl(clients);"); 613 | writer.EndBlock(); 614 | writer.WriteLine(""); 615 | 616 | // Generate the proxy class 617 | writer.WriteLine($@"private class {interfaceType}Impl : {interfaceType}"); 618 | writer.StartBlock(); 619 | writer.WriteLine("private Microsoft.AspNetCore.SignalR.IClientProxy Proxy { get; }"); 620 | writer.WriteLine($"public {interfaceType}Impl(Microsoft.AspNetCore.SignalR.IClientProxy proxy) => Proxy = proxy;"); 621 | foreach (var m in interfaceType.GetMethods()) 622 | { 623 | writer.Write($"public {m.ReturnType} {m.Name}("); 624 | var parameters = m.GetParameters(); 625 | foreach (var p in parameters) 626 | { 627 | if (p.Position > 0) 628 | { 629 | writer.WriteNoIndent(", "); 630 | } 631 | writer.WriteNoIndent($"{p.ParameterType} {p.Name}"); 632 | } 633 | writer.WriteNoIndent(")"); 634 | writer.WriteNoIndent($@" => Proxy.SendCoreAsync(""{m.Name}"""); 635 | foreach (var p in parameters) 636 | { 637 | writer.WriteNoIndent(", "); 638 | if (p.Position == 0) 639 | { 640 | writer.WriteNoIndent("new object[] {"); 641 | } 642 | writer.WriteNoIndent(p.Name); 643 | } 644 | 645 | if (parameters.Length > 0) 646 | { 647 | writer.WriteNoIndent("}"); 648 | } 649 | else 650 | { 651 | writer.WriteNoIndent("System.Array.Empty()"); 652 | } 653 | writer.WriteLineNoIndent(");"); 654 | } 655 | writer.EndBlock(); 656 | writer.WriteLine(""); 657 | 658 | // Generate the hub caller clients impl 659 | writer.WriteLine($@"private class {t.Name}ClientsImpl : Microsoft.AspNetCore.SignalR.IHubCallerClients<{interfaceType}>"); 660 | writer.StartBlock(); 661 | writer.WriteLine("private readonly Microsoft.AspNetCore.SignalR.IHubCallerClients _clients;"); 662 | 663 | writer.WriteLine($"public {t.Name}ClientsImpl(Microsoft.AspNetCore.SignalR.IHubCallerClients clients) => _clients = clients;"); 664 | writer.WriteLine(""); 665 | 666 | // Get all the properties and methods in the interface hierarchy 667 | foreach (var hct in hubCallerClientsTypes) 668 | { 669 | foreach (var p in hct.GetProperties()) 670 | { 671 | writer.WriteLine($"public {interfaceType} {p.Name} => new {interfaceType}Impl(_clients.{p.Name});"); 672 | writer.WriteLine(""); 673 | } 674 | } 675 | 676 | foreach (var hct in hubCallerClientsTypes) 677 | { 678 | foreach (var m in hct.GetMethods()) 679 | { 680 | // Is special name isn't working yet 681 | if (m.IsSpecialName) continue; 682 | 683 | writer.Write($"public {interfaceType} {m.Name}("); 684 | var parameters = m.GetParameters(); 685 | foreach (var p in parameters) 686 | { 687 | if (p.Position > 0) 688 | { 689 | writer.WriteNoIndent(", "); 690 | } 691 | writer.WriteNoIndent($"{p.ParameterType} {p.Name}"); 692 | } 693 | writer.WriteNoIndent(")"); 694 | writer.WriteNoIndent($@" => new {interfaceType}Impl(_clients.{m.Name}("); 695 | foreach (var p in parameters) 696 | { 697 | if (p.Position > 0) 698 | { 699 | writer.WriteNoIndent(","); 700 | } 701 | writer.WriteNoIndent(p.Name); 702 | } 703 | writer.WriteLineNoIndent("));"); 704 | } 705 | } 706 | writer.WriteLine(""); 707 | writer.EndBlock(); 708 | } 709 | 710 | writer.WriteLine(""); 711 | writer.WriteLine(@$"public static void BindHub(Microsoft.AspNetCore.SignalR.IHubDefinition definition)"); 712 | writer.StartBlock(); 713 | 714 | if (interfaceType is not null) 715 | { 716 | writer.WriteLine("definition.SetHubInitializer(InitializeHub);"); 717 | } 718 | 719 | foreach (var (method, thunk) in generatedMethods) 720 | { 721 | writer.WriteLine($@"definition.AddHubMethod(""{method}"", {thunk});"); 722 | } 723 | writer.EndBlock(); 724 | 725 | writer.EndBlock(); 726 | 727 | if (!globalNs) 728 | { 729 | writer.EndBlock(); 730 | } 731 | } 732 | 733 | context.AddSource("Hubs.g.cs", SourceText.From(sb.ToString().Trim(), Encoding.UTF8)); 734 | 735 | 736 | bool IsAsyncEnumerable(Type type) 737 | { 738 | return type.IsGenericType && type.GetGenericTypeDefinition().Equals(asyncEnumerable); 739 | } 740 | 741 | bool IsHubMethod(MethodInfo methodInfo) 742 | { 743 | var baseDefinition = methodInfo.GetBaseDefinition().DeclaringType!; 744 | if (baseDefinition.Equals(typeof(object)) || methodInfo.IsSpecialName) 745 | { 746 | return false; 747 | } 748 | 749 | var baseType = baseDefinition.IsGenericType ? baseDefinition.GetGenericTypeDefinition() : baseDefinition; 750 | return !hubType.Equals(baseType); 751 | } 752 | 753 | bool IsHubOfT(Type t, out Type interfaceType) 754 | { 755 | if (t.BaseType?.IsGenericType == true && 756 | t.BaseType.GetGenericTypeDefinition() == hubOfTType) 757 | { 758 | interfaceType = t.BaseType.GetGenericArguments()[0]; 759 | return true; 760 | } 761 | interfaceType = null; 762 | return false; 763 | } 764 | } 765 | 766 | public void Initialize(GeneratorInitializationContext context) 767 | { 768 | context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); 769 | } 770 | 771 | private class SyntaxReceiver : ISyntaxReceiver 772 | { 773 | public List MapMiddleware { get; } = new(); 774 | 775 | public void OnVisitSyntaxNode(SyntaxNode syntaxNode) 776 | { 777 | if (syntaxNode is InvocationExpressionSyntax 778 | { 779 | Expression: MemberAccessExpressionSyntax 780 | { 781 | Name: GenericNameSyntax 782 | { 783 | Identifier.ValueText: "UseMiddleware" 784 | } 785 | } 786 | } useMiddleware) 787 | { 788 | MapMiddleware.Add(useMiddleware); 789 | } 790 | } 791 | } 792 | } 793 | 794 | class Diagnostics 795 | { 796 | public static readonly DiagnosticDescriptor MissingInvokeMethod = new DiagnosticDescriptor("MID001", "MissingInvokeMethod", "Missing Invoke method {0}", "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true); 797 | } 798 | } 799 | -------------------------------------------------------------------------------- /SourceGeneratorPlayground.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33025.470 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGeneratorPlayground", "SourceGeneratorPlayground\SourceGeneratorPlayground.csproj", "{0D437A64-3C9E-432E-96ED-7F4BE6F964B1}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGenExperiments", "SourceGenExperiments\SourceGenExperiments.csproj", "{EEE2D3BD-AEF0-422B-854C-1EEFA64750F8}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {0D437A64-3C9E-432E-96ED-7F4BE6F964B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {0D437A64-3C9E-432E-96ED-7F4BE6F964B1}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {0D437A64-3C9E-432E-96ED-7F4BE6F964B1}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {0D437A64-3C9E-432E-96ED-7F4BE6F964B1}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {EEE2D3BD-AEF0-422B-854C-1EEFA64750F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {EEE2D3BD-AEF0-422B-854C-1EEFA64750F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {EEE2D3BD-AEF0-422B-854C-1EEFA64750F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {EEE2D3BD-AEF0-422B-854C-1EEFA64750F8}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {29F91124-37C1-40AE-8997-2B92BC70021C} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /SourceGeneratorPlayground/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | //namespace SourceGeneratorPlayground; 4 | 5 | public partial class HomeController : ControllerBase 6 | { 7 | [HttpGet("/")] 8 | public string Get() => "Hello World"; 9 | } 10 | -------------------------------------------------------------------------------- /SourceGeneratorPlayground/Hubs/ChatHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | 3 | partial class ChatHub : Hub 4 | { 5 | public Task Send(string message) 6 | { 7 | return Clients.All.Send(message); 8 | } 9 | 10 | public async IAsyncEnumerable Loop(int many, CancellationToken cancellationToken) 11 | { 12 | for (int i = 0; i < many; i++) 13 | { 14 | yield return i; 15 | await Task.Delay(1000, cancellationToken); 16 | } 17 | } 18 | 19 | public async Task UploadData(string name, DateTime date, IAsyncEnumerable blobs) 20 | { 21 | await foreach (var item in blobs) 22 | { 23 | 24 | } 25 | } 26 | } 27 | 28 | interface IClient 29 | { 30 | Task Send(string message); 31 | } -------------------------------------------------------------------------------- /SourceGeneratorPlayground/Hubs/MessagesHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | 3 | namespace SourceGeneratorPlayground 4 | { 5 | public partial class MessagesHub : Hub 6 | { 7 | public Task GroupSend(string group, string message) 8 | { 9 | return Clients.Group(group).SendAsync(message); 10 | } 11 | 12 | public async IAsyncEnumerable EchoNumbers(IAsyncEnumerable message, CancellationToken cancellationToken) 13 | { 14 | await foreach (var item in message.WithCancellation(cancellationToken)) 15 | { 16 | yield return item; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SourceGeneratorPlayground/Program.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Microsoft.Extensions.Options; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | var app = builder.Build(); 7 | 8 | app.MapPost("/posts", (Post p) => Results.Ok(p)); 9 | 10 | app.UseMiddleware(); 11 | 12 | app.Run(); 13 | 14 | public class MyOptions 15 | { 16 | 17 | } 18 | 19 | struct Post 20 | { 21 | [Required] 22 | public string Title { get; set; } 23 | } 24 | 25 | partial class MyMiddleware 26 | { 27 | private readonly RequestDelegate _next; 28 | 29 | public MyMiddleware(RequestDelegate next) 30 | { 31 | _next = next; 32 | } 33 | 34 | public Task Invoke(HttpContext context, IOptions options) 35 | { 36 | return _next(context); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SourceGeneratorPlayground/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:5391", 7 | "sslPort": 44380 8 | } 9 | }, 10 | "profiles": { 11 | "http": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": false, 15 | "applicationUrl": "http://localhost:5174", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "https": { 21 | "commandName": "Project", 22 | "dotnetRunMessages": true, 23 | "launchBrowser": false, 24 | "applicationUrl": "https://localhost:7068;http://localhost:5174", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | }, 29 | "IIS Express": { 30 | "commandName": "IISExpress", 31 | "launchBrowser": true, 32 | "environmentVariables": { 33 | "ASPNETCORE_ENVIRONMENT": "Development" 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SourceGeneratorPlayground/SourceGeneratorPlayground.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /SourceGeneratorPlayground/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SourceGeneratorPlayground/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | --------------------------------------------------------------------------------