| File: RequestDelegateGenerator.Emitter.cs | Web Access |
| Project: src\aspnetcore\src\Http\Http.Extensions\gen\Microsoft.AspNetCore.Http.RequestDelegateGenerator\Microsoft.AspNetCore.Http.RequestDelegateGenerator.csproj (Microsoft.AspNetCore.Http.RequestDelegateGenerator) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Linq; using Microsoft.AspNetCore.Analyzers.Infrastructure; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel.Emitters; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator; public sealed partial class RequestDelegateGenerator : IIncrementalGenerator { internal static string EmitInterceptorDefinition((Endpoint Source, int Index, ImmutableArray<InterceptableLocation> Elements) endpointWithLocations) { var endpoint = endpointWithLocations.Source; using var stringWriter = new StringWriter(CultureInfo.InvariantCulture); using var codeWriter = new CodeWriter(stringWriter, baseIndent: 2); foreach (var location in endpointWithLocations.Elements) { #pragma warning disable RSEXPERIMENTAL002 // Experimental interceptable location API codeWriter.WriteLine(location.GetInterceptsLocationAttributeSyntax()); #pragma warning restore RSEXPERIMENTAL002 } codeWriter.WriteLine($"internal static RouteHandlerBuilder {endpoint.HttpMethod}{endpointWithLocations.Index}("); codeWriter.Indent++; codeWriter.WriteLine("this IEndpointRouteBuilder endpoints,"); // MapFallback overloads that only take a delegate do not need a pattern argument var hasPatternParameter = endpoint.HttpMethod != "MapFallback" || endpoint.Operation.Arguments.Length != 2; if (hasPatternParameter) { codeWriter.WriteLine(@"[StringSyntax(""Route"")] string pattern,"); } // MapMethods overloads define an additional `httpMethods` parameter if (endpoint.HttpMethod == "MapMethods") { codeWriter.WriteLine("IEnumerable<string> httpMethods,"); } codeWriter.WriteLine("Delegate handler)"); codeWriter.Indent--; codeWriter.StartBlock(); codeWriter.WriteLine("MetadataPopulator populateMetadata = (methodInfo, options) =>"); codeWriter.StartBlock(); codeWriter.WriteLine(@"Debug.Assert(options != null, ""RequestDelegateFactoryOptions not found."");"); codeWriter.WriteLine(@"Debug.Assert(options.EndpointBuilder != null, ""EndpointBuilder not found."");"); codeWriter.WriteLine($"options.EndpointBuilder.Metadata.Add(new {RequestDelegateGeneratorSources.GeneratedCodeConstructor});"); endpoint.EmitEndpointMetadataPopulation(codeWriter); codeWriter.WriteLine("return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() };"); codeWriter.EndBlockWithSemicolon(); codeWriter.WriteLine("RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) =>"); codeWriter.StartBlock(); codeWriter.WriteLine(@"Debug.Assert(options != null, ""RequestDelegateFactoryOptions not found."");"); codeWriter.WriteLine(@"Debug.Assert(options.EndpointBuilder != null, ""EndpointBuilder not found."");"); codeWriter.WriteLine(@"Debug.Assert(options.EndpointBuilder.ApplicationServices != null, ""ApplicationServices not found."");"); codeWriter.WriteLine(@"Debug.Assert(options.EndpointBuilder.FilterFactories != null, ""FilterFactories not found."");"); codeWriter.WriteLine($"var handler = Cast(del, {endpoint.EmitHandlerDelegateType()} => throw null!);"); codeWriter.WriteLine("EndpointFilterDelegate? filteredInvocation = null;"); codeWriter.WriteLine("var serviceProvider = options.ServiceProvider ?? options.EndpointBuilder.ApplicationServices;"); endpoint.EmitLoggingPreamble(codeWriter); endpoint.EmitJsonPreparation(codeWriter); endpoint.EmitRouteOrQueryResolver(codeWriter); endpoint.EmitJsonBodyOrServiceResolver(codeWriter); if (endpoint.NeedsParameterArray) { codeWriter.WriteLine("var parameters = del.Method.GetParameters();"); } codeWriter.WriteLineNoTabs(string.Empty); codeWriter.WriteLine("if (options.EndpointBuilder.FilterFactories.Count > 0)"); codeWriter.StartBlock(); codeWriter.WriteLine(endpoint.Response?.IsAwaitable == true ? "filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(async ic =>" : "filteredInvocation = GeneratedRouteBuilderExtensionsCore.BuildFilterDelegate(ic =>"); codeWriter.StartBlock(); codeWriter.WriteLine("if (ic.HttpContext.Response.StatusCode == 400)"); codeWriter.StartBlock(); codeWriter.WriteLine(endpoint.Response?.IsAwaitable == true ? "return (object?)Results.Empty;" : "return ValueTask.FromResult<object?>(Results.Empty);"); codeWriter.EndBlock(); endpoint.EmitFilteredInvocation(codeWriter); codeWriter.EndBlockWithComma(); codeWriter.WriteLine("options.EndpointBuilder,"); codeWriter.WriteLine("handler.Method);"); codeWriter.EndBlock(); codeWriter.WriteLineNoTabs(string.Empty); endpoint.EmitRequestHandler(codeWriter); codeWriter.WriteLineNoTabs(string.Empty); endpoint.EmitFilteredRequestHandler(codeWriter); codeWriter.WriteLineNoTabs(string.Empty); codeWriter.WriteLine("RequestDelegate targetDelegate = filteredInvocation is null ? RequestHandler : RequestHandlerFiltered;"); codeWriter.WriteLine("var metadata = inferredMetadataResult?.EndpointMetadata ?? ReadOnlyCollection<object>.Empty;"); codeWriter.WriteLine("return new RequestDelegateResult(targetDelegate, metadata);"); codeWriter.EndBlockWithSemicolon(); codeWriter.WriteLine($"var castHandler = Cast(handler, {endpoint.EmitHandlerDelegateType()} => throw null!);"); codeWriter.WriteLine("return MapCore("); codeWriter.Indent++; codeWriter.WriteLine("endpoints,"); // For `MapFallback` overloads that only take a delegate, provide the assumed default // Otherwise, pass the pattern provided from the MapX invocation if (hasPatternParameter) { codeWriter.WriteLine("pattern,"); } else { codeWriter.WriteLine($"{SymbolDisplay.FormatLiteral("{*path:nonfile}", true)},"); } codeWriter.WriteLine("handler,"); codeWriter.WriteLine($"{endpoint.EmitVerb()},"); codeWriter.WriteLine("populateMetadata,"); codeWriter.WriteLine("createRequestDelegate,"); codeWriter.WriteLine("castHandler.Method);"); codeWriter.Indent--; codeWriter.EndBlock(); return stringWriter.ToString(); } internal static ImmutableHashSet<string> EmitHttpVerbs(ImmutableArray<Endpoint> endpoints) { return endpoints .Distinct(EndpointHttpMethodComparer.Instance) .Select(endpoint => endpoint.EmitterContext.HttpMethod!) .Where(verb => verb is not null) .ToImmutableHashSet(); } internal static string EmitEndpointHelpers(ImmutableArray<Endpoint> endpoints) { var hasJsonBodyOrService = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBodyOrService); var hasJsonBodyOrQuery = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBodyOrQuery); var hasJsonBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBody); var hasFormBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasFormBody); var hasRouteOrQuery = endpoints.Any(endpoint => endpoint.EmitterContext.HasRouteOrQuery); var hasBindAsync = endpoints.Any(endpoint => endpoint.EmitterContext.HasBindAsync); var hasParsable = endpoints.Any(endpoint => endpoint.EmitterContext.HasParsable); var hasEndpointMetadataProvider = endpoints.Any(endpoint => endpoint.EmitterContext.HasEndpointMetadataProvider); var hasEndpointParameterMetadataProvider = endpoints.Any(endpoint => endpoint.EmitterContext.HasEndpointParameterMetadataProvider); var hasIResult = endpoints.Any(endpoint => endpoint.Response?.IsIResult == true); using var stringWriter = new StringWriter(CultureInfo.InvariantCulture); using var codeWriter = new CodeWriter(stringWriter, baseIndent: 0); if (hasRouteOrQuery) { codeWriter.WriteLine(RequestDelegateGeneratorSources.ResolveFromRouteOrQueryMethod); } if (hasJsonBody || hasJsonBodyOrService || hasJsonBodyOrQuery) { codeWriter.WriteLine(RequestDelegateGeneratorSources.TryResolveBodyAsyncMethod); } if (hasFormBody) { codeWriter.WriteLine(RequestDelegateGeneratorSources.TryResolveFormAsyncMethod); } if (hasBindAsync) { codeWriter.WriteLine(RequestDelegateGeneratorSources.BindAsyncMethod); } if (hasJsonBodyOrService) { codeWriter.WriteLine(RequestDelegateGeneratorSources.ResolveJsonBodyOrServiceMethod); } if (hasParsable) { codeWriter.WriteLine(RequestDelegateGeneratorSources.TryParseExplicitMethod); } if (hasIResult) { codeWriter.WriteLine(RequestDelegateGeneratorSources.ExecuteAsyncExplicitMethod); } if (hasEndpointMetadataProvider) { codeWriter.WriteLine(RequestDelegateGeneratorSources.PopulateEndpointMetadataMethod); } if (hasEndpointParameterMetadataProvider) { codeWriter.WriteLine(RequestDelegateGeneratorSources.PopulateEndpointParameterMetadataMethod); } return stringWriter.ToString(); } internal static string EmitHelperTypes(ImmutableArray<Endpoint> endpoints) { var hasFormBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasFormBody); var hasJsonBody = endpoints.Any(endpoint => endpoint.EmitterContext.HasJsonBody || endpoint.EmitterContext.HasJsonBodyOrService || endpoint.EmitterContext.HasJsonBodyOrQuery); var hasResponseMetadata = endpoints.Any(endpoint => endpoint.EmitterContext.HasResponseMetadata); var requiresPropertyAsParameterInfo = endpoints.Any(endpoint => endpoint.EmitterContext.RequiresPropertyAsParameterInfo); var requiresParameterBindingMetadataClass = endpoints.Any(endpoint => endpoint.EmitterContext.RequiresParameterBindingMetadataClass); using var stringWriter = new StringWriter(CultureInfo.InvariantCulture); using var codeWriter = new CodeWriter(stringWriter, baseIndent: 0); if (hasFormBody) { codeWriter.WriteLine(RequestDelegateGeneratorSources.AntiforgeryMetadataClass); } if (hasJsonBody || hasResponseMetadata) { codeWriter.WriteLine(RequestDelegateGeneratorSources.DisableCookieRedirectMetadataClass); } if (hasFormBody || hasJsonBody || hasResponseMetadata) { codeWriter.WriteLine(RequestDelegateGeneratorSources.ContentTypeConstantsType); } if (requiresPropertyAsParameterInfo) { codeWriter.WriteLine(RequestDelegateGeneratorSources.PropertyAsParameterInfoClass); } if (requiresParameterBindingMetadataClass) { codeWriter.WriteLine(RequestDelegateGeneratorSources.ParameterBindingMetadataClass); } return stringWriter.ToString(); } internal static void Emit( SourceProductionContext context, ImmutableArray<string> endpointsCode, string helperMethods, ImmutableHashSet<string> httpVerbs, string helperTypes) { if (endpointsCode.IsDefaultOrEmpty) { return; } using var stringWriter = new StringWriter(CultureInfo.InvariantCulture); using var codeWriter = new CodeWriter(stringWriter, baseIndent: 0); foreach (var endpoint in endpointsCode) { codeWriter.WriteLine(endpoint); } var code = RequestDelegateGeneratorSources.GetGeneratedRouteBuilderExtensionsSource( endpoints: stringWriter.ToString(), helperMethods: helperMethods ?? string.Empty, helperTypes: helperTypes ?? string.Empty, verbs: httpVerbs); context.AddSource("GeneratedRouteBuilderExtensions.g.cs", code); } }