From c71c225fc2829743c1e1d440114d19fe9e76df92 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 22 Nov 2023 14:17:16 -0800 Subject: [PATCH 01/10] Adding support for executing functions from referenced assemblies, to the source generated executor implementaion. --- .../FunctionExecutorGenerator.Emitter.cs | 103 ++++++++---- ...ionExecutorGenerator.ExecutableFunction.cs | 5 + .../FunctionExecutorGenerator.Parser.cs | 32 ++-- .../FunctionExecutorGenerator.cs | 38 ++++- .../FunctionMethodVisibility.cs | 26 +++ sdk/Sdk.Generators/MethodVisibilityChecker.cs | 45 ++++++ ...crosoft.Azure.Functions.Worker.Sdk.targets | 3 +- .../FooInternalClass.cs | 18 +++ .../InternalFunction.cs | 15 -- .../FunctionExecutor/DependentAssemblyTest.cs | 153 ++++++++++++++++++ .../FunctionExecutorGeneratorTests.cs | 10 +- test/Sdk.Generator.Tests/TestHelpers.cs | 38 ++++- 12 files changed, 405 insertions(+), 81 deletions(-) create mode 100644 sdk/Sdk.Generators/FunctionMethodVisibility.cs create mode 100644 sdk/Sdk.Generators/MethodVisibilityChecker.cs create mode 100644 test/DependentAssemblyWithFunctions/FooInternalClass.cs delete mode 100644 test/DependentAssemblyWithFunctions/InternalFunction.cs create mode 100644 test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs index 2d4c3e5fc..767f9e93a 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs @@ -31,6 +31,7 @@ namespace {{FunctionsUtil.GetNamespaceForGeneratedCode(context)}} [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor { + private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; {{GetTypesDictionary(functions)}} public DirectFunctionExecutor(IFunctionActivator functionActivator) @@ -41,7 +42,7 @@ public DirectFunctionExecutor(IFunctionActivator functionActivator) /// public async ValueTask ExecuteAsync(FunctionContext context) { - {{GetMethodBody(functions)}} + {{GetMethodBody(functions, context)}} } } @@ -114,7 +115,7 @@ public void Configure(IHostBuilder hostBuilder) return ""; } - private static string GetMethodBody(IEnumerable functions) + private static string GetMethodBody(IEnumerable functions, GeneratorExecutionContext context) { var sb = new StringBuilder(); sb.Append( @@ -125,53 +126,91 @@ private static string GetMethodBody(IEnumerable functions) """); bool first = true; - foreach (ExecutableFunction function in functions) + foreach (ExecutableFunction function in functions.Where(f=>f.Visibility!= FunctionMethodVisibility.NotPublic)) { + var fast = function.Visibility == FunctionMethodVisibility.PublicAndVisible ? true: false; sb.Append($$""" {{(first ? string.Empty : "else ")}}if (string.Equals(context.FunctionDefinition.EntryPoint, "{{function.EntryPoint}}", StringComparison.Ordinal)) { + {{(fast ? EmitFastPath(function) : EmitSlowPath(function, context))}} + } """); - first = false; - int functionParamCounter = 0; - var functionParamList = new List(); - foreach (var argumentTypeName in function.ParameterTypeNames) - { - functionParamList.Add($"({argumentTypeName})inputArguments[{functionParamCounter++}]"); - } - var methodParamsStr = string.Join(", ", functionParamList); - - if (!function.IsStatic) - { - sb.Append($$""" - - var instanceType = types["{{function.ParentFunctionClassName}}"]; + } + + return sb.ToString(); + } + + private static string EmitFastPath(ExecutableFunction function) + { + var sb = new StringBuilder(); + int functionParamCounter = 0; + var functionParamList = new List(); + foreach (var argumentTypeName in function.ParameterTypeNames) + { + functionParamList.Add($"({argumentTypeName})inputArguments[{functionParamCounter++}]"); + } + var methodParamsStr = string.Join(", ", functionParamList); + + if (!function.IsStatic) + { + sb.Append($$""" + var instanceType = types["{{function.ParentFunctionClassName}}"]; var i = _functionActivator.CreateInstance(instanceType, context) as {{function.ParentFunctionFullyQualifiedClassName}}; """); - } + } + if (!function.IsStatic) + { sb.Append(@" "); + } + else + { + sb.Append($$""" """); + } - if (function.IsReturnValueAssignable) - { - sb.Append(@$"context.GetInvocationResult().Value = "); - } - if (function.ShouldAwait) - { - sb.Append("await "); - } - - sb.Append(function.IsStatic - ? @$"{function.ParentFunctionFullyQualifiedClassName}.{function.MethodName}({methodParamsStr}); - }}" - : $@"i.{function.MethodName}({methodParamsStr}); - }}"); + if (function.IsReturnValueAssignable) + { + sb.Append(@$"context.GetInvocationResult().Value = "); + } + if (function.ShouldAwait) + { + sb.Append("await "); } + sb.Append(function.IsStatic + ? @$"{function.ParentFunctionFullyQualifiedClassName}.{function.MethodName}({methodParamsStr});" + : $@"i.{function.MethodName}({methodParamsStr});"); return sb.ToString(); } + + private static string EmitSlowPath(ExecutableFunction function, GeneratorExecutionContext context) + { + return + $$""" + if (_defaultExecutor == null) + { + var t = Type.GetType("{{GetDefaultExecutorFullName(context.Compilation)}}"); + _defaultExecutor = ActivatorUtilities.CreateInstance(context.InstanceServices, t) as Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; + } + await _defaultExecutor.ExecuteAsync(context); + """; + } + + /// + /// Returns the fully qualified name of the default function executor type + /// Ex: Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c + /// + private static string GetDefaultExecutorFullName(Compilation compilation) + { + var coreAssembly = compilation.SourceModule.ReferencedAssemblySymbols.Where(a => a.Name == "Microsoft.Azure.Functions.Worker.Core").Single(); + // ex: Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c + + var className = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor"; + return $"{className}, {coreAssembly.OriginalDefinition}"; + } } } } diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs index e9f1f16f0..499d95c62 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs @@ -52,5 +52,10 @@ internal class ExecutableFunction /// A collection of fully qualified type names of the parameters of the function. /// internal IEnumerable ParameterTypeNames { set; get; } = Enumerable.Empty(); + + /// + /// Get a value indicating the visibility of the executable function. + /// + internal FunctionMethodVisibility Visibility { get; set; } } } diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs index fc7ccffa1..bcd674cc2 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.Azure.Functions.Worker.Sdk.Generators { @@ -23,48 +21,38 @@ internal Parser(GeneratorExecutionContext context) private Compilation Compilation => _context.Compilation; - internal ICollection GetFunctions(List methods) + internal ICollection GetFunctions(IEnumerable methods) { var functionList = new List(); - foreach (MethodDeclarationSyntax method in methods) + foreach (IMethodSymbol method in methods) { _context.CancellationToken.ThrowIfCancellationRequested(); - var model = Compilation.GetSemanticModel(method.SyntaxTree); - if (!FunctionsUtil.IsValidFunctionMethod(_context, Compilation, model, method)) - { - continue; - } + var methodName = method.Name; + var methodParameterList = new List(); - var methodName = method.Identifier.Text; - var methodParameterList = new List(method.ParameterList.Parameters.Count); - - foreach (var methodParam in method.ParameterList.Parameters) + foreach (IParameterSymbol parameterSymbol in method.Parameters) { - if (model.GetDeclaredSymbol(methodParam) is not IParameterSymbol parameterSymbol) - { - continue; - } - var fullyQualifiedTypeName = parameterSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); methodParameterList.Add(fullyQualifiedTypeName); } - var methodSymbol = model.GetDeclaredSymbol(method)!; + var methodSymbol = method; var defaultFormatClassName = methodSymbol.ContainingSymbol.ToDisplayString(); var fullyQualifiedClassName = methodSymbol.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); var function = new ExecutableFunction { - EntryPoint = $"{defaultFormatClassName}.{method.Identifier.ValueText}", + EntryPoint = $"{defaultFormatClassName}.{method.Name}", ParameterTypeNames = methodParameterList, MethodName = methodName, ShouldAwait = IsTaskType(methodSymbol.ReturnType), IsReturnValueAssignable = IsReturnValueAssignable(methodSymbol), - IsStatic = method.Modifiers.Any(SyntaxKind.StaticKeyword), + IsStatic = method.IsStatic, ParentFunctionClassName = defaultFormatClassName, - ParentFunctionFullyQualifiedClassName = fullyQualifiedClassName + ParentFunctionFullyQualifiedClassName = fullyQualifiedClassName, + Visibility = MethodVisibilityChecker.GetVisibility(methodSymbol), }; functionList.Add(function); diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs index 4bbea665f..03bbd4afc 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs @@ -1,8 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System.Collections.Generic; +using System.Linq; using System.Text; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using FunctionMethodSyntaxReceiver = Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionMetadataProviderGenerator.FunctionMethodSyntaxReceiver; @@ -23,25 +26,52 @@ public void Execute(GeneratorExecutionContext context) return; } - if (context.SyntaxReceiver is not FunctionMethodSyntaxReceiver receiver || receiver.CandidateMethods.Count == 0) + if (context.SyntaxReceiver is not FunctionMethodSyntaxReceiver receiver) { return; } - var parser = new Parser(context); - var functions = parser.GetFunctions(receiver.CandidateMethods); + var entryAssemblyFuncs = GetSymbolsMethodSyntaxes(receiver.CandidateMethods, context); + var dependentFuncs = GetDependentAssemblyFunctionsSymbols(context); + var allMethods = entryAssemblyFuncs.Concat(dependentFuncs); - if (functions.Count == 0) + if (!allMethods.Any()) { return; } + var parser = new Parser(context); + var functions = parser.GetFunctions(allMethods); var shouldIncludeAutoGeneratedAttributes = ShouldIncludeAutoGeneratedAttributes(context); var text = Emitter.Emit(context, functions, shouldIncludeAutoGeneratedAttributes); context.AddSource(Constants.FileNames.GeneratedFunctionExecutor, SourceText.From(text, Encoding.UTF8)); } + private IEnumerable GetSymbolsMethodSyntaxes(List methods, GeneratorExecutionContext context) + { + foreach (MethodDeclarationSyntax method in methods) + { + var model = context.Compilation.GetSemanticModel(method.SyntaxTree); + + if (FunctionsUtil.IsValidFunctionMethod(context, context.Compilation, model, method)) + { + IMethodSymbol? methodSymbol = (IMethodSymbol)model.GetDeclaredSymbol(method)!; + yield return methodSymbol; + } + } + } + + /// + /// Collect methods with Function attributes on them from dependent/referenced assemblies. + /// + private IEnumerable GetDependentAssemblyFunctionsSymbols(GeneratorExecutionContext context) + { + var visitor = new ReferencedAssemblyMethodVisitor(context.Compilation); + visitor.Visit(context.Compilation.SourceModule); + + return visitor.FunctionMethods; + } private static bool ShouldIncludeAutoGeneratedAttributes(GeneratorExecutionContext context) { if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue( diff --git a/sdk/Sdk.Generators/FunctionMethodVisibility.cs b/sdk/Sdk.Generators/FunctionMethodVisibility.cs new file mode 100644 index 000000000..ec4f15b68 --- /dev/null +++ b/sdk/Sdk.Generators/FunctionMethodVisibility.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.Azure.Functions.Worker.Sdk.Generators +{ + /// + /// Represents the visibility of a "azure function" method and it's parent classes. + /// + internal enum FunctionMethodVisibility + { + /// + /// The method and it's parent classes are public. + /// + PublicAndVisible, + + /// + /// The method is public, but one or more of it's parent classes are not public. + /// + PublicButContainingTypeNotVisible, + + /// + /// The method is not public. + /// + NotPublic + } +} diff --git a/sdk/Sdk.Generators/MethodVisibilityChecker.cs b/sdk/Sdk.Generators/MethodVisibilityChecker.cs new file mode 100644 index 000000000..c0255d5a5 --- /dev/null +++ b/sdk/Sdk.Generators/MethodVisibilityChecker.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; + +namespace Microsoft.Azure.Functions.Worker.Sdk.Generators +{ + internal static class MethodVisibilityChecker + { + /// + /// Determines the visibility of a method. + /// The visibility of a method is determined by the following rules: + /// 1. If the method is public, and all containing types are public, return PublicAndVisible + /// 2. If the method is public, but one or more containing types are not public, return PublicButContainingTypeNotVisible + /// 3. If the method is not public, return NotPublic + /// + /// + /// + internal static FunctionMethodVisibility GetVisibility(IMethodSymbol methodSymbol) + { + // Check if the symbol itself is public + if (methodSymbol.DeclaredAccessibility == Accessibility.Public) + { + // Check if any containing type is not public + INamedTypeSymbol containingType = methodSymbol.ContainingType; + while (containingType != null) + { + if (containingType.DeclaredAccessibility != Accessibility.Public) + { + return FunctionMethodVisibility.PublicButContainingTypeNotVisible; + } + containingType = containingType.ContainingType; + } + + // If both the symbol and all containing types are public, return PublicAndVisible + return FunctionMethodVisibility.PublicAndVisible; + } + else + { + // If the symbol itself is not public, return NotPublic + return FunctionMethodVisibility.NotPublic; + } + } + } +} diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets index 8ff55d245..206d5f6b0 100644 --- a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets +++ b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets @@ -38,8 +38,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and true $(FunctionsEnableWorkerIndexing) $(FunctionsEnableWorkerIndexing) - - false + true true true diff --git a/test/DependentAssemblyWithFunctions/FooInternalClass.cs b/test/DependentAssemblyWithFunctions/FooInternalClass.cs new file mode 100644 index 000000000..3be33a6fd --- /dev/null +++ b/test/DependentAssemblyWithFunctions/FooInternalClass.cs @@ -0,0 +1,18 @@ +// (c).NET Foundation.All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; + +namespace DependentAssemblyWithFunctions +{ + internal class FooInternalClass + { + [Function("InternalClassPublicRun")] + public static HttpResponseData PublicRun([HttpTrigger(AuthorizationLevel.Admin, "get")] HttpRequestData r, + FunctionContext executionContext) + { + throw new NotImplementedException(); + } + } +} diff --git a/test/DependentAssemblyWithFunctions/InternalFunction.cs b/test/DependentAssemblyWithFunctions/InternalFunction.cs deleted file mode 100644 index 6a34b7388..000000000 --- a/test/DependentAssemblyWithFunctions/InternalFunction.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Http; - -namespace DependentAssemblyWithFunctions -{ - internal class InternalFunction - { - [Function(nameof(InternalFunction))] - public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req, - FunctionContext executionContext) - { - throw new NotImplementedException(); - } - } -} diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs new file mode 100644 index 000000000..b54ab9b6d --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs @@ -0,0 +1,153 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using System.Threading.Tasks; +using Azure.Messaging.EventHubs; +using Azure.Storage.Queues.Models; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Sdk.Generators; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Microsoft.Azure.Functions.SdkGeneratorTests +{ + public partial class FunctionExecutorGeneratorTests + { + public class DependentAssemblyTest + { + private readonly Assembly[] _referencedAssemblies; + + public DependentAssemblyTest() + { + var abstractionsExtension = Assembly.LoadFrom("Microsoft.Azure.Functions.Worker.Extensions.Abstractions.dll"); + var httpExtension = Assembly.LoadFrom("Microsoft.Azure.Functions.Worker.Extensions.Http.dll"); + var hostingExtension = Assembly.LoadFrom("Microsoft.Extensions.Hosting.dll"); + var diExtension = Assembly.LoadFrom("Microsoft.Extensions.DependencyInjection.dll"); + var hostingAbExtension = Assembly.LoadFrom("Microsoft.Extensions.Hosting.Abstractions.dll"); + var diAbExtension = Assembly.LoadFrom("Microsoft.Extensions.DependencyInjection.Abstractions.dll"); + var dependentAssembly = Assembly.LoadFrom("DependentAssemblyWithFunctions.dll"); + + _referencedAssemblies = new[] + { + abstractionsExtension, + httpExtension, + hostingExtension, + hostingAbExtension, + diExtension, + diAbExtension, + dependentAssembly + }; + } + + [Fact] + public async Task FunctionsFromDependentAssembly() + { + const string inputSourceCode = @" +using System; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +namespace MyCompany +{ + public class MyHttpTriggers + { + [Function(""FunctionA"")] + public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + } +} +"; + var expectedOutput = $@"// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace TestProject +{{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + internal class DirectFunctionExecutor : IFunctionExecutor + {{ + private IFunctionExecutor _defaultExecutor; + private readonly IFunctionActivator _functionActivator; + private readonly Dictionary types = new() + {{ + {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers"")! }}, + {{ ""DependentAssemblyWithFunctions.DependencyFunction"", Type.GetType(""DependentAssemblyWithFunctions.DependencyFunction"")! }}, + {{ ""MyCompany.MyProduct.MyApp.HttpFunctions"", Type.GetType(""MyCompany.MyProduct.MyApp.HttpFunctions"")! }}, + {{ ""MyCompany.MyProduct.MyApp.Foo.Bar"", Type.GetType(""MyCompany.MyProduct.MyApp.Foo.Bar"")! }} + }}; + + public DirectFunctionExecutor(IFunctionActivator functionActivator) + {{ + _functionActivator = functionActivator ?? throw new ArgumentNullException(nameof(functionActivator)); + }} + + /// + public async ValueTask ExecuteAsync(FunctionContext context) + {{ + var inputBindingFeature = context.Features.Get()!; + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context)!; + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Foo"", StringComparison.Ordinal)) + {{ + var instanceType = types[""MyCompany.MyHttpTriggers""]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + }} + else if (string.Equals(context.FunctionDefinition.EntryPoint, ""DependentAssemblyWithFunctions.DependencyFunction.Run"", StringComparison.Ordinal)) + {{ + var instanceType = types[""DependentAssemblyWithFunctions.DependencyFunction""]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::DependentAssemblyWithFunctions.DependencyFunction; + context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + }} + else if (string.Equals(context.FunctionDefinition.EntryPoint, ""DependentAssemblyWithFunctions.InternalFunction.Run"", StringComparison.Ordinal)) + {{ + if (_defaultExecutor == null) + {{ + var t = Type.GetType(""Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c""); + _defaultExecutor = ActivatorUtilities.CreateInstance(context.InstanceServices, t) as Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; + }} + await _defaultExecutor.ExecuteAsync(context); + }} + else if (string.Equals(context.FunctionDefinition.EntryPoint, ""DependentAssemblyWithFunctions.StaticFunction.Run"", StringComparison.Ordinal)) + {{ + context.GetInvocationResult().Value = global::DependentAssemblyWithFunctions.StaticFunction.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + }} + else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyProduct.MyApp.HttpFunctions.Run"", StringComparison.Ordinal)) + {{ + var instanceType = types[""MyCompany.MyProduct.MyApp.HttpFunctions""]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyProduct.MyApp.HttpFunctions; + context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + }} + else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyProduct.MyApp.Foo.Bar.Run"", StringComparison.Ordinal)) + {{ + var instanceType = types[""MyCompany.MyProduct.MyApp.Foo.Bar""]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyProduct.MyApp.Foo.Bar; + context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + }} + }} + }} +{GetExpectedExtensionMethodCode()} +}}".Replace("'", "\""); + + await TestHelpers.RunTestAsync( + _referencedAssemblies, + inputSourceCode, + Constants.FileNames.GeneratedFunctionExecutor, + expectedOutput); + } + } + } +} diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs index 46b4a461b..beb50188a 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.Azure.Functions.SdkGeneratorTests { - public class FunctionExecutorGeneratorTests + public partial class FunctionExecutorGeneratorTests { // A super set of assemblies we need for all tests in the file. private readonly Assembly[] _referencedAssemblies = new[] @@ -113,6 +113,7 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ + private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -222,6 +223,7 @@ namespace MyCompany.MyProject.MyApp [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ + private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -359,6 +361,7 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ + private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; public DirectFunctionExecutor(IFunctionActivator functionActivator) @@ -468,6 +471,7 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ + private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -553,6 +557,7 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ + private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -636,6 +641,7 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ + private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -676,7 +682,7 @@ public async ValueTask ExecuteAsync(FunctionContext context) expectedOutput); } - private static string GetExpectedExtensionMethodCode(bool includeAutoStartupType = false) + public static string GetExpectedExtensionMethodCode(bool includeAutoStartupType = false) { if (includeAutoStartupType) { diff --git a/test/Sdk.Generator.Tests/TestHelpers.cs b/test/Sdk.Generator.Tests/TestHelpers.cs index c1d015ebc..5c464ecb9 100644 --- a/test/Sdk.Generator.Tests/TestHelpers.cs +++ b/test/Sdk.Generator.Tests/TestHelpers.cs @@ -15,6 +15,8 @@ using Microsoft.Azure.Functions.Worker.Core; using System.Reflection; using System.Collections.Generic; +using System.Linq; +using System.Runtime.Versioning; namespace Microsoft.Azure.Functions.SdkGeneratorTests { @@ -86,11 +88,16 @@ public class Test : CSharpSourceGeneratorTest { public Test() { - // See https://www.nuget.org/packages/Microsoft.NETCore.App.Ref/6.0.0 + var targetFrameworkAttribute = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(TargetFrameworkAttribute), false) + .SingleOrDefault() as TargetFrameworkAttribute; + + string targetFramework = targetFrameworkAttribute!.FrameworkName; + var tfm = ConvertFrameworkMonikerToTfm(targetFramework); + this.ReferenceAssemblies = new ReferenceAssemblies( - targetFramework: "net6.0", - referenceAssemblyPackage: new PackageIdentity("Microsoft.NETCore.App.Ref", "6.0.0"), - referenceAssemblyPath: Path.Combine("ref", "net6.0")); + targetFramework: tfm, + referenceAssemblyPackage: new PackageIdentity("Microsoft.NETCore.App.Ref", Environment.Version.ToString()), + referenceAssemblyPath: Path.Combine("ref", tfm)); } public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.CSharp9; @@ -118,6 +125,29 @@ protected override ParseOptions CreateParseOptions() { return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(this.LanguageVersion); } + + /// + /// Example input: .NETCoreApp,Version=v7.0 + /// Example output: net7.0 + /// + private static string ConvertFrameworkMonikerToTfm(string frameworkMoniker) + { + var parts = frameworkMoniker.Split(','); + var identifier = parts[0]; + var version = parts[1].Split('=')[1]; + + switch (identifier) + { + case ".NETCoreApp": + return $"net{version.Substring(1)}"; + case ".NETFramework": + return $"net{version.Replace(".", "")}"; + case ".NETStandard": + return $"netstandard{version.Substring(1)}"; + default: + throw new NotSupportedException($"Unknown framework identifier: {identifier}"); + } + } } } } From 99b26ca4c30f88db8cb6ea91b6d4d30c37244a9e Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 22 Nov 2023 14:28:26 -0800 Subject: [PATCH 02/10] Release note and version bump --- sdk/Sdk.Generators/Sdk.Generators.csproj | 2 +- sdk/release_notes.md | 6 +++++- .../{FooInternalClass.cs => InternalFunction.cs} | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) rename test/DependentAssemblyWithFunctions/{FooInternalClass.cs => InternalFunction.cs} (66%) diff --git a/sdk/Sdk.Generators/Sdk.Generators.csproj b/sdk/Sdk.Generators/Sdk.Generators.csproj index 6d312db62..cbe6ac94e 100644 --- a/sdk/Sdk.Generators/Sdk.Generators.csproj +++ b/sdk/Sdk.Generators/Sdk.Generators.csproj @@ -10,7 +10,7 @@ false true 1 - 4 + 5 true diff --git a/sdk/release_notes.md b/sdk/release_notes.md index dd5cf7a3f..1a45bbf94 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -3,7 +3,11 @@ - ### Microsoft.Azure.Functions.Worker.Sdk 1.16.3 (meta package) - Update worker.config generation to accurate worker executable name (#1053) +- Update Microsoft.Azure.Functions.Worker.Sdk.Generators dependency to 1.1.5 +- +### Microsoft.Azure.Functions.Worker.Sdk.Generators dependency to 1.1.5 + +- Adding support for executing functions from referenced assemblies in the optimized executor (#2089) diff --git a/test/DependentAssemblyWithFunctions/FooInternalClass.cs b/test/DependentAssemblyWithFunctions/InternalFunction.cs similarity index 66% rename from test/DependentAssemblyWithFunctions/FooInternalClass.cs rename to test/DependentAssemblyWithFunctions/InternalFunction.cs index 3be33a6fd..7b1149473 100644 --- a/test/DependentAssemblyWithFunctions/FooInternalClass.cs +++ b/test/DependentAssemblyWithFunctions/InternalFunction.cs @@ -6,10 +6,10 @@ namespace DependentAssemblyWithFunctions { - internal class FooInternalClass + internal class InternalFunction { - [Function("InternalClassPublicRun")] - public static HttpResponseData PublicRun([HttpTrigger(AuthorizationLevel.Admin, "get")] HttpRequestData r, + [Function(nameof(InternalFunction))] + public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req, FunctionContext executionContext) { throw new NotImplementedException(); From dcb8e15428ba83c0f4a401ec3ba27553711c8be5 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 22 Nov 2023 14:43:53 -0800 Subject: [PATCH 03/10] Picking only public functions as valid functions for metadata generation and executor. --- .../FunctionExecutor/FunctionExecutorGenerator.Parser.cs | 3 ++- .../FunctionMetadataProviderGenerator.Parser.cs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs index bcd674cc2..0fb14fdc0 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Linq; using Microsoft.CodeAnalysis; namespace Microsoft.Azure.Functions.Worker.Sdk.Generators @@ -25,7 +26,7 @@ internal ICollection GetFunctions(IEnumerable { var functionList = new List(); - foreach (IMethodSymbol method in methods) + foreach (IMethodSymbol method in methods.Where(m=>m.DeclaredAccessibility == Accessibility.Public)) { _context.CancellationToken.ThrowIfCancellationRequested(); diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs index 3a49c95a1..4091645f5 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs @@ -44,8 +44,8 @@ public IReadOnlyList GetFunctionMetadataInfo(List(); - // Loop through the candidate methods (methods with any attribute associated with them) - foreach (IMethodSymbol method in methods) + // Loop through the candidate methods (methods with any attribute associated with them) which are public. + foreach (IMethodSymbol method in methods.Where(m => m.DeclaredAccessibility == Accessibility.Public)) { CancellationToken.ThrowIfCancellationRequested(); @@ -129,7 +129,7 @@ private bool TryGetBindings(IMethodSymbol method, out IList /// Checks for and returns any OutputBinding attributes associated with the method. /// - private bool TryGetMethodOutputBinding(IMethodSymbol method,out bool hasOutputBinding, out GeneratorRetryOptions? retryOptions, out IList>? bindingsList) + private bool TryGetMethodOutputBinding(IMethodSymbol method, out bool hasOutputBinding, out GeneratorRetryOptions? retryOptions, out IList>? bindingsList) { var attributes = method!.GetAttributes(); // methodSymbol is not null here because it's checked in IsValidAzureFunction which is called before bindings are collected/created From 7395e122421c3ccf5a2b54df14f3b4002f817fd0 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 22 Nov 2023 15:00:30 -0800 Subject: [PATCH 04/10] Minor cleanup --- .../FunctionExecutor/FunctionExecutorGenerator.Emitter.cs | 7 +++---- sdk/release_notes.md | 5 +++-- test/DependentAssemblyWithFunctions/InternalFunction.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs index 767f9e93a..e815eb5f4 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs @@ -126,9 +126,9 @@ private static string GetMethodBody(IEnumerable functions, G """); bool first = true; - foreach (ExecutableFunction function in functions.Where(f=>f.Visibility!= FunctionMethodVisibility.NotPublic)) + foreach (ExecutableFunction function in functions) { - var fast = function.Visibility == FunctionMethodVisibility.PublicAndVisible ? true: false; + var fast = function.Visibility == FunctionMethodVisibility.PublicAndVisible ? true : false; sb.Append($$""" {{(first ? string.Empty : "else ")}}if (string.Equals(context.FunctionDefinition.EntryPoint, "{{function.EntryPoint}}", StringComparison.Ordinal)) @@ -206,9 +206,8 @@ private static string EmitSlowPath(ExecutableFunction function, GeneratorExecuti private static string GetDefaultExecutorFullName(Compilation compilation) { var coreAssembly = compilation.SourceModule.ReferencedAssemblySymbols.Where(a => a.Name == "Microsoft.Azure.Functions.Worker.Core").Single(); - // ex: Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c - var className = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor"; + return $"{className}, {coreAssembly.OriginalDefinition}"; } } diff --git a/sdk/release_notes.md b/sdk/release_notes.md index 1a45bbf94..c1ca125f3 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -6,8 +6,9 @@ ### Microsoft.Azure.Functions.Worker.Sdk 1.16.3 (meta package) - Update worker.config generation to accurate worker executable name (#1053) +- Default to optimized function executor. - Update Microsoft.Azure.Functions.Worker.Sdk.Generators dependency to 1.1.5 -- + ### Microsoft.Azure.Functions.Worker.Sdk.Generators dependency to 1.1.5 -- Adding support for executing functions from referenced assemblies in the optimized executor (#2089) +- Adding support for executing functions from referenced assemblies in the optimized function executor (#2089) diff --git a/test/DependentAssemblyWithFunctions/InternalFunction.cs b/test/DependentAssemblyWithFunctions/InternalFunction.cs index 7b1149473..a92b46159 100644 --- a/test/DependentAssemblyWithFunctions/InternalFunction.cs +++ b/test/DependentAssemblyWithFunctions/InternalFunction.cs @@ -9,7 +9,7 @@ namespace DependentAssemblyWithFunctions internal class InternalFunction { [Function(nameof(InternalFunction))] - public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req, + public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req, FunctionContext executionContext) { throw new NotImplementedException(); From 5e584886868fb517ad038649f078a6022fac6a13 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 22 Nov 2023 19:26:00 -0800 Subject: [PATCH 05/10] Get full type name including assembly identity for all types. --- .../FunctionExecutorGenerator.Emitter.cs | 10 +++++++--- ...FunctionExecutorGenerator.ExecutableFunction.cs | 6 ++++++ .../FunctionExecutorGenerator.Parser.cs | 1 + .../FunctionExecutor/DependentAssemblyTest.cs | 8 ++++---- .../FunctionExecutorGeneratorTests.cs | 14 +++++++------- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs index e815eb5f4..e3bcdac25 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs @@ -70,8 +70,12 @@ public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder private static string GetTypesDictionary(IEnumerable functions) { - var classNames = functions.Where(f => !f.IsStatic).Select(f => f.ParentFunctionClassName).Distinct(); - if (!classNames.Any()) + var typesDict = functions + .Where(f => !f.IsStatic) + .GroupBy(f => f.ParentFunctionClassName) + .ToDictionary(k => k.First().ParentFunctionClassName, v => v.First().AssemblyIdentity); + + if (typesDict.Count == 0) { return """ @@ -81,7 +85,7 @@ private static string GetTypesDictionary(IEnumerable functio return $$""" private readonly Dictionary types = new() { - {{string.Join($",{Environment.NewLine} ", classNames.Select(c => $$""" { "{{c}}", Type.GetType("{{c}}")! }"""))}} + {{string.Join($",{Environment.NewLine} ", typesDict.Select(c => $$""" { "{{c.Key}}", Type.GetType("{{c.Key}}, {{c.Value}}")! }"""))}} }; """; diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs index 499d95c62..d9982cb18 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs @@ -57,5 +57,11 @@ internal class ExecutableFunction /// Get a value indicating the visibility of the executable function. /// internal FunctionMethodVisibility Visibility { get; set; } + + /// + /// Gets the assembly identity of the function. + /// ex: FooAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null + /// + internal string AssemblyIdentity { get; set; } = null!; } } diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs index 0fb14fdc0..ac6288635 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs @@ -54,6 +54,7 @@ internal ICollection GetFunctions(IEnumerable ParentFunctionClassName = defaultFormatClassName, ParentFunctionFullyQualifiedClassName = fullyQualifiedClassName, Visibility = MethodVisibilityChecker.GetVisibility(methodSymbol), + AssemblyIdentity = methodSymbol.ContainingAssembly.Identity.GetDisplayName(), }; functionList.Add(function); diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs index b54ab9b6d..b78ff4d7c 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs @@ -82,10 +82,10 @@ internal class DirectFunctionExecutor : IFunctionExecutor private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers"")! }}, - {{ ""DependentAssemblyWithFunctions.DependencyFunction"", Type.GetType(""DependentAssemblyWithFunctions.DependencyFunction"")! }}, - {{ ""MyCompany.MyProduct.MyApp.HttpFunctions"", Type.GetType(""MyCompany.MyProduct.MyApp.HttpFunctions"")! }}, - {{ ""MyCompany.MyProduct.MyApp.Foo.Bar"", Type.GetType(""MyCompany.MyProduct.MyApp.Foo.Bar"")! }} + {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, + {{ ""DependentAssemblyWithFunctions.DependencyFunction"", Type.GetType(""DependentAssemblyWithFunctions.DependencyFunction, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, + {{ ""MyCompany.MyProduct.MyApp.HttpFunctions"", Type.GetType(""MyCompany.MyProduct.MyApp.HttpFunctions, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, + {{ ""MyCompany.MyProduct.MyApp.Foo.Bar"", Type.GetType(""MyCompany.MyProduct.MyApp.Foo.Bar, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"")! }} }}; public DirectFunctionExecutor(IFunctionActivator functionActivator) diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs index beb50188a..0ac72de13 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs @@ -117,9 +117,9 @@ internal class DirectFunctionExecutor : IFunctionExecutor private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers"")! }}, - {{ ""MyCompany.MyHttpTriggers2"", Type.GetType(""MyCompany.MyHttpTriggers2"")! }}, - {{ ""MyCompany.QueueTriggers"", Type.GetType(""MyCompany.QueueTriggers"")! }} + {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, + {{ ""MyCompany.MyHttpTriggers2"", Type.GetType(""MyCompany.MyHttpTriggers2, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, + {{ ""MyCompany.QueueTriggers"", Type.GetType(""MyCompany.QueueTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }} }}; public DirectFunctionExecutor(IFunctionActivator functionActivator) @@ -227,7 +227,7 @@ internal class DirectFunctionExecutor : IFunctionExecutor private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers"")! }} + {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }} }}; public DirectFunctionExecutor(IFunctionActivator functionActivator) @@ -475,7 +475,7 @@ internal class DirectFunctionExecutor : IFunctionExecutor private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers"")! }} + {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }} }}; public DirectFunctionExecutor(IFunctionActivator functionActivator) @@ -561,7 +561,7 @@ internal class DirectFunctionExecutor : IFunctionExecutor private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ - {{ ""TestProject.TestProject"", Type.GetType(""TestProject.TestProject"")! }} + {{ ""TestProject.TestProject"", Type.GetType(""TestProject.TestProject, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }} }}; public DirectFunctionExecutor(IFunctionActivator functionActivator) @@ -645,7 +645,7 @@ internal class DirectFunctionExecutor : IFunctionExecutor private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers"")! }} + {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }} }}; public DirectFunctionExecutor(IFunctionActivator functionActivator) From d7db5a5fc9164c32bd72d031d770de15d4902d53 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 22 Nov 2023 19:39:08 -0800 Subject: [PATCH 06/10] Minor cleanup --- sdk/Sdk.Generators/MethodVisibilityChecker.cs | 8 +++----- .../FunctionExecutor/FunctionExecutorGeneratorTests.cs | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sdk/Sdk.Generators/MethodVisibilityChecker.cs b/sdk/Sdk.Generators/MethodVisibilityChecker.cs index c0255d5a5..b14018810 100644 --- a/sdk/Sdk.Generators/MethodVisibilityChecker.cs +++ b/sdk/Sdk.Generators/MethodVisibilityChecker.cs @@ -35,11 +35,9 @@ internal static FunctionMethodVisibility GetVisibility(IMethodSymbol methodSymbo // If both the symbol and all containing types are public, return PublicAndVisible return FunctionMethodVisibility.PublicAndVisible; } - else - { - // If the symbol itself is not public, return NotPublic - return FunctionMethodVisibility.NotPublic; - } + + // If the symbol itself is not public, return NotPublic + return FunctionMethodVisibility.NotPublic; } } } diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs index 0ac72de13..12daf10bf 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs @@ -682,7 +682,7 @@ public async ValueTask ExecuteAsync(FunctionContext context) expectedOutput); } - public static string GetExpectedExtensionMethodCode(bool includeAutoStartupType = false) + private static string GetExpectedExtensionMethodCode(bool includeAutoStartupType = false) { if (includeAutoStartupType) { From 1dddbe139d3887a003475f32273cbc6b023f2965 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Fri, 24 Nov 2023 20:09:06 -0800 Subject: [PATCH 07/10] Switching initializing the defaultExecutor to a Lazy approach. --- .../FunctionExecutorGenerator.Emitter.cs | 55 ++++++++++--------- .../FunctionExecutor/DependentAssemblyTest.cs | 18 +++--- .../FunctionExecutorGeneratorTests.cs | 6 -- 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs index e3bcdac25..220b99fc5 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs @@ -15,6 +15,7 @@ internal static class Emitter { internal static string Emit(GeneratorExecutionContext context, IEnumerable functions, bool includeAutoRegistrationCode) { + var anyDefaultExecutor = functions.Any(f => f.Visibility == FunctionMethodVisibility.PublicButContainingTypeNotVisible); string result = $$""" // @@ -31,8 +32,7 @@ namespace {{FunctionsUtil.GetNamespaceForGeneratedCode(context)}} [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor { - private IFunctionExecutor _defaultExecutor; - private readonly IFunctionActivator _functionActivator; + private readonly IFunctionActivator _functionActivator;{{(anyDefaultExecutor ? $"{Environment.NewLine} private Lazy _defaultExecutor;" : string.Empty)}} {{GetTypesDictionary(functions)}} public DirectFunctionExecutor(IFunctionActivator functionActivator) { @@ -42,8 +42,8 @@ public DirectFunctionExecutor(IFunctionActivator functionActivator) /// public async ValueTask ExecuteAsync(FunctionContext context) { - {{GetMethodBody(functions, context)}} - } + {{GetMethodBody(functions, context, anyDefaultExecutor)}} + }{{(anyDefaultExecutor ? $"{Environment.NewLine}{GetLazyDefaultExecutorMethod(context)}" : "")}} } /// @@ -68,6 +68,25 @@ public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder return result; } + private static string GetLazyDefaultExecutorMethod(GeneratorExecutionContext context) + { + var coreAssembly = context.Compilation.SourceModule.ReferencedAssemblySymbols.Where(a => a.Name == "Microsoft.Azure.Functions.Worker.Core").Single(); + var assemblyIdentity = coreAssembly.Identity; + + var sb = $$""" + + private IFunctionExecutor GetDefaultExecutor(FunctionContext context) + { + var defaultExecutorFullName = $"Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, {{assemblyIdentity}}"; + var defaultExecutorType = Type.GetType("defaultExecutorFullName"); + + return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; + } + """; + + return sb; + } + private static string GetTypesDictionary(IEnumerable functions) { var typesDict = functions @@ -119,17 +138,20 @@ public void Configure(IHostBuilder hostBuilder) return ""; } - private static string GetMethodBody(IEnumerable functions, GeneratorExecutionContext context) + private static string GetMethodBody(IEnumerable functions, GeneratorExecutionContext context, bool anyDefaultExecutor) { var sb = new StringBuilder(); sb.Append( - """ + $$""" var inputBindingFeature = context.Features.Get()!; var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context)!; var inputArguments = inputBindingResult.Values; - + {{(anyDefaultExecutor ? $" _defaultExecutor = new Lazy(() => GetDefaultExecutor(context));{Environment.NewLine}" : string.Empty)}} """); + bool first = true; + + foreach (ExecutableFunction function in functions) { var fast = function.Visibility == FunctionMethodVisibility.PublicAndVisible ? true : false; @@ -194,26 +216,9 @@ private static string EmitSlowPath(ExecutableFunction function, GeneratorExecuti { return $$""" - if (_defaultExecutor == null) - { - var t = Type.GetType("{{GetDefaultExecutorFullName(context.Compilation)}}"); - _defaultExecutor = ActivatorUtilities.CreateInstance(context.InstanceServices, t) as Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; - } - await _defaultExecutor.ExecuteAsync(context); + await _defaultExecutor.Value.ExecuteAsync(context); """; } - - /// - /// Returns the fully qualified name of the default function executor type - /// Ex: Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c - /// - private static string GetDefaultExecutorFullName(Compilation compilation) - { - var coreAssembly = compilation.SourceModule.ReferencedAssemblySymbols.Where(a => a.Name == "Microsoft.Azure.Functions.Worker.Core").Single(); - var className = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor"; - - return $"{className}, {coreAssembly.OriginalDefinition}"; - } } } } diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs index b78ff4d7c..c10f46fa6 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs @@ -78,8 +78,8 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ - private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; + private Lazy _defaultExecutor; private readonly Dictionary types = new() {{ {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, @@ -99,6 +99,7 @@ public async ValueTask ExecuteAsync(FunctionContext context) var inputBindingFeature = context.Features.Get()!; var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context)!; var inputArguments = inputBindingResult.Values; + _defaultExecutor = new Lazy(() => GetDefaultExecutor(context)); if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Foo"", StringComparison.Ordinal)) {{ @@ -114,12 +115,7 @@ public async ValueTask ExecuteAsync(FunctionContext context) }} else if (string.Equals(context.FunctionDefinition.EntryPoint, ""DependentAssemblyWithFunctions.InternalFunction.Run"", StringComparison.Ordinal)) {{ - if (_defaultExecutor == null) - {{ - var t = Type.GetType(""Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c""); - _defaultExecutor = ActivatorUtilities.CreateInstance(context.InstanceServices, t) as Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; - }} - await _defaultExecutor.ExecuteAsync(context); + await _defaultExecutor.Value.ExecuteAsync(context); }} else if (string.Equals(context.FunctionDefinition.EntryPoint, ""DependentAssemblyWithFunctions.StaticFunction.Run"", StringComparison.Ordinal)) {{ @@ -138,6 +134,14 @@ public async ValueTask ExecuteAsync(FunctionContext context) context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); }} }} + + private IFunctionExecutor GetDefaultExecutor(FunctionContext context) + {{ + var defaultExecutorFullName = $""Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c""; + var defaultExecutorType = Type.GetType(""defaultExecutorFullName""); + + return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; + }} }} {GetExpectedExtensionMethodCode()} }}".Replace("'", "\""); diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs index 12daf10bf..fcda1c27b 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs @@ -113,7 +113,6 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ - private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -223,7 +222,6 @@ namespace MyCompany.MyProject.MyApp [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ - private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -361,7 +359,6 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ - private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; public DirectFunctionExecutor(IFunctionActivator functionActivator) @@ -471,7 +468,6 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ - private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -557,7 +553,6 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ - private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ @@ -641,7 +636,6 @@ namespace TestProject [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor {{ - private IFunctionExecutor _defaultExecutor; private readonly IFunctionActivator _functionActivator; private readonly Dictionary types = new() {{ From 3051803d632c92fd98a7bdf657c6369350bb04ff Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Sat, 25 Nov 2023 13:28:11 -0800 Subject: [PATCH 08/10] A bit more cleanup. --- .../FunctionExecutorGenerator.Emitter.cs | 57 +++-- ...ionExecutorGenerator.ExecutableFunction.cs | 2 +- .../FunctionExecutorGenerator.Parser.cs | 13 +- .../FunctionExecutorGenerator.cs | 2 +- .../InternalFunction.cs | 6 + .../FunctionExecutor/DependentAssemblyTest.cs | 207 +++++++++--------- 6 files changed, 140 insertions(+), 147 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs index 220b99fc5..4422a85dc 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs @@ -13,9 +13,10 @@ public partial class FunctionExecutorGenerator { internal static class Emitter { - internal static string Emit(GeneratorExecutionContext context, IEnumerable functions, bool includeAutoRegistrationCode) + internal static string Emit(GeneratorExecutionContext context, IEnumerable executableFunctions, bool includeAutoRegistrationCode) { - var anyDefaultExecutor = functions.Any(f => f.Visibility == FunctionMethodVisibility.PublicButContainingTypeNotVisible); + var functions = executableFunctions.ToList(); + var defaultExecutorNeeded = functions.Any(f => f.Visibility == FunctionMethodVisibility.PublicButContainingTypeNotVisible); string result = $$""" // @@ -32,7 +33,7 @@ namespace {{FunctionsUtil.GetNamespaceForGeneratedCode(context)}} [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] internal class DirectFunctionExecutor : IFunctionExecutor { - private readonly IFunctionActivator _functionActivator;{{(anyDefaultExecutor ? $"{Environment.NewLine} private Lazy _defaultExecutor;" : string.Empty)}} + private readonly IFunctionActivator _functionActivator;{{(defaultExecutorNeeded ? $"{Environment.NewLine} private Lazy _defaultExecutor;" : string.Empty)}} {{GetTypesDictionary(functions)}} public DirectFunctionExecutor(IFunctionActivator functionActivator) { @@ -42,8 +43,8 @@ public DirectFunctionExecutor(IFunctionActivator functionActivator) /// public async ValueTask ExecuteAsync(FunctionContext context) { - {{GetMethodBody(functions, context, anyDefaultExecutor)}} - }{{(anyDefaultExecutor ? $"{Environment.NewLine}{GetLazyDefaultExecutorMethod(context)}" : "")}} + {{GetMethodBody(functions, defaultExecutorNeeded)}} + }{{(defaultExecutorNeeded ? $"{Environment.NewLine}{EmitCreateDefaultExecutorMethod(context)}" : string.Empty)}} } /// @@ -68,27 +69,26 @@ public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder return result; } - private static string GetLazyDefaultExecutorMethod(GeneratorExecutionContext context) + private static string EmitCreateDefaultExecutorMethod(GeneratorExecutionContext context) { - var coreAssembly = context.Compilation.SourceModule.ReferencedAssemblySymbols.Where(a => a.Name == "Microsoft.Azure.Functions.Worker.Core").Single(); - var assemblyIdentity = coreAssembly.Identity; + var workerCoreAssembly = context.Compilation.SourceModule.ReferencedAssemblySymbols.Single(a => a.Name == "Microsoft.Azure.Functions.Worker.Core"); + var assemblyIdentity = workerCoreAssembly.Identity; - var sb = $$""" + return $$""" - private IFunctionExecutor GetDefaultExecutor(FunctionContext context) + private IFunctionExecutor CreateDefaultExecutorInstance(FunctionContext context) { - var defaultExecutorFullName = $"Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, {{assemblyIdentity}}"; - var defaultExecutorType = Type.GetType("defaultExecutorFullName"); + var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, {{assemblyIdentity}}"; + var defaultExecutorType = Type.GetType(defaultExecutorFullName); - return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; + return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as IFunctionExecutor; } """; - - return sb; } private static string GetTypesDictionary(IEnumerable functions) { + // Build a dictionary of type names and it's full qualified names (including assembly identity) var typesDict = functions .Where(f => !f.IsStatic) .GroupBy(f => f.ParentFunctionClassName) @@ -96,9 +96,7 @@ private static string GetTypesDictionary(IEnumerable functio if (typesDict.Count == 0) { - return """ - - """; + return ""; } return $$""" @@ -138,7 +136,7 @@ public void Configure(IHostBuilder hostBuilder) return ""; } - private static string GetMethodBody(IEnumerable functions, GeneratorExecutionContext context, bool anyDefaultExecutor) + private static string GetMethodBody(IEnumerable functions, bool anyDefaultExecutor) { var sb = new StringBuilder(); sb.Append( @@ -146,20 +144,19 @@ private static string GetMethodBody(IEnumerable functions, G var inputBindingFeature = context.Features.Get()!; var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context)!; var inputArguments = inputBindingResult.Values; - {{(anyDefaultExecutor ? $" _defaultExecutor = new Lazy(() => GetDefaultExecutor(context));{Environment.NewLine}" : string.Empty)}} + {{(anyDefaultExecutor ? $" _defaultExecutor = new Lazy(() => CreateDefaultExecutorInstance(context));{Environment.NewLine}" : string.Empty)}} """); bool first = true; - foreach (ExecutableFunction function in functions) { - var fast = function.Visibility == FunctionMethodVisibility.PublicAndVisible ? true : false; + var fast = function.Visibility == FunctionMethodVisibility.PublicAndVisible; sb.Append($$""" {{(first ? string.Empty : "else ")}}if (string.Equals(context.FunctionDefinition.EntryPoint, "{{function.EntryPoint}}", StringComparison.Ordinal)) { - {{(fast ? EmitFastPath(function) : EmitSlowPath(function, context))}} + {{(fast ? EmitFastPath(function) : EmitSlowPath())}} } """); first = false; @@ -194,12 +191,12 @@ private static string EmitFastPath(ExecutableFunction function) } else { - sb.Append($$""" """); + sb.Append(" "); } if (function.IsReturnValueAssignable) { - sb.Append(@$"context.GetInvocationResult().Value = "); + sb.Append("context.GetInvocationResult().Value = "); } if (function.ShouldAwait) { @@ -207,17 +204,15 @@ private static string EmitFastPath(ExecutableFunction function) } sb.Append(function.IsStatic - ? @$"{function.ParentFunctionFullyQualifiedClassName}.{function.MethodName}({methodParamsStr});" - : $@"i.{function.MethodName}({methodParamsStr});"); + ? $"{function.ParentFunctionFullyQualifiedClassName}.{function.MethodName}({methodParamsStr});" + : $"i.{function.MethodName}({methodParamsStr});"); return sb.ToString(); } - private static string EmitSlowPath(ExecutableFunction function, GeneratorExecutionContext context) + private static string EmitSlowPath() { return - $$""" - await _defaultExecutor.Value.ExecuteAsync(context); - """; + " await _defaultExecutor.Value.ExecuteAsync(context);"; } } } diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs index d9982cb18..2dbcfe7b6 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs @@ -60,7 +60,7 @@ internal class ExecutableFunction /// /// Gets the assembly identity of the function. - /// ex: FooAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null + /// ex: FooAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=9475d07f10cb09df /// internal string AssemblyIdentity { get; set; } = null!; } diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs index ac6288635..42d68bfc1 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs @@ -39,22 +39,21 @@ internal ICollection GetFunctions(IEnumerable methodParameterList.Add(fullyQualifiedTypeName); } - var methodSymbol = method; - var defaultFormatClassName = methodSymbol.ContainingSymbol.ToDisplayString(); - var fullyQualifiedClassName = methodSymbol.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + var defaultFormatClassName = method.ContainingSymbol.ToDisplayString(); + var fullyQualifiedClassName = method.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); var function = new ExecutableFunction { EntryPoint = $"{defaultFormatClassName}.{method.Name}", ParameterTypeNames = methodParameterList, MethodName = methodName, - ShouldAwait = IsTaskType(methodSymbol.ReturnType), - IsReturnValueAssignable = IsReturnValueAssignable(methodSymbol), + ShouldAwait = IsTaskType(method.ReturnType), + IsReturnValueAssignable = IsReturnValueAssignable(method), IsStatic = method.IsStatic, ParentFunctionClassName = defaultFormatClassName, ParentFunctionFullyQualifiedClassName = fullyQualifiedClassName, - Visibility = MethodVisibilityChecker.GetVisibility(methodSymbol), - AssemblyIdentity = methodSymbol.ContainingAssembly.Identity.GetDisplayName(), + Visibility = MethodVisibilityChecker.GetVisibility(method), + AssemblyIdentity = method.ContainingAssembly.Identity.GetDisplayName(), }; functionList.Add(function); diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs index 03bbd4afc..c822848f8 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs @@ -65,7 +65,7 @@ private IEnumerable GetSymbolsMethodSyntaxes(List /// Collect methods with Function attributes on them from dependent/referenced assemblies. /// - private IEnumerable GetDependentAssemblyFunctionsSymbols(GeneratorExecutionContext context) + private static IEnumerable GetDependentAssemblyFunctionsSymbols(GeneratorExecutionContext context) { var visitor = new ReferencedAssemblyMethodVisitor(context.Compilation); visitor.Visit(context.Compilation.SourceModule); diff --git a/test/DependentAssemblyWithFunctions/InternalFunction.cs b/test/DependentAssemblyWithFunctions/InternalFunction.cs index a92b46159..5cf10a5c2 100644 --- a/test/DependentAssemblyWithFunctions/InternalFunction.cs +++ b/test/DependentAssemblyWithFunctions/InternalFunction.cs @@ -14,5 +14,11 @@ public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "g { throw new NotImplementedException(); } + + [Function("ThisShouldBeSkippedBecauseMethodNotPublic")] + internal static HttpResponseData Run2([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req) + { + throw new NotImplementedException(); + } } } diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs index c10f46fa6..e9094d064 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs @@ -1,18 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System; using System.Reflection; using System.Threading.Tasks; -using Azure.Messaging.EventHubs; -using Azure.Storage.Queues.Models; -using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Sdk.Generators; -using Microsoft.CodeAnalysis.Testing; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; using Xunit; namespace Microsoft.Azure.Functions.SdkGeneratorTests @@ -48,109 +39,111 @@ public DependentAssemblyTest() [Fact] public async Task FunctionsFromDependentAssembly() { - const string inputSourceCode = @" -using System; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Http; -namespace MyCompany -{ - public class MyHttpTriggers - { - [Function(""FunctionA"")] - public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r, FunctionContext c) - { - return r.CreateResponse(System.Net.HttpStatusCode.OK); - } - } -} -"; - var expectedOutput = $@"// -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Context.Features; -using Microsoft.Azure.Functions.Worker.Invocation; -namespace TestProject -{{ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - internal class DirectFunctionExecutor : IFunctionExecutor - {{ - private readonly IFunctionActivator _functionActivator; - private Lazy _defaultExecutor; - private readonly Dictionary types = new() - {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, - {{ ""DependentAssemblyWithFunctions.DependencyFunction"", Type.GetType(""DependentAssemblyWithFunctions.DependencyFunction, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, - {{ ""MyCompany.MyProduct.MyApp.HttpFunctions"", Type.GetType(""MyCompany.MyProduct.MyApp.HttpFunctions, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"")! }}, - {{ ""MyCompany.MyProduct.MyApp.Foo.Bar"", Type.GetType(""MyCompany.MyProduct.MyApp.Foo.Bar, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"")! }} - }}; - - public DirectFunctionExecutor(IFunctionActivator functionActivator) - {{ - _functionActivator = functionActivator ?? throw new ArgumentNullException(nameof(functionActivator)); - }} - - /// - public async ValueTask ExecuteAsync(FunctionContext context) - {{ - var inputBindingFeature = context.Features.Get()!; - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context)!; - var inputArguments = inputBindingResult.Values; - _defaultExecutor = new Lazy(() => GetDefaultExecutor(context)); - - if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Foo"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyHttpTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; - context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""DependentAssemblyWithFunctions.DependencyFunction.Run"", StringComparison.Ordinal)) - {{ - var instanceType = types[""DependentAssemblyWithFunctions.DependencyFunction""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::DependentAssemblyWithFunctions.DependencyFunction; - context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""DependentAssemblyWithFunctions.InternalFunction.Run"", StringComparison.Ordinal)) - {{ - await _defaultExecutor.Value.ExecuteAsync(context); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""DependentAssemblyWithFunctions.StaticFunction.Run"", StringComparison.Ordinal)) - {{ - context.GetInvocationResult().Value = global::DependentAssemblyWithFunctions.StaticFunction.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyProduct.MyApp.HttpFunctions.Run"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyProduct.MyApp.HttpFunctions""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyProduct.MyApp.HttpFunctions; - context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - }} - else if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyProduct.MyApp.Foo.Bar.Run"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyProduct.MyApp.Foo.Bar""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyProduct.MyApp.Foo.Bar; - context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - }} - }} - - private IFunctionExecutor GetDefaultExecutor(FunctionContext context) - {{ - var defaultExecutorFullName = $""Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c""; - var defaultExecutorType = Type.GetType(""defaultExecutorFullName""); - - return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; - }} - }} -{GetExpectedExtensionMethodCode()} -}}".Replace("'", "\""); + const string inputSourceCode = """ + using System; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + namespace MyCompany + { + public class MyHttpTriggers + { + [Function("FunctionA")] + public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, "get")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + } + } + """; + var expected = $$""" + // + using System; + using System.Threading.Tasks; + using System.Collections.Generic; + using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Context.Features; + using Microsoft.Azure.Functions.Worker.Invocation; + namespace TestProject + { + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + internal class DirectFunctionExecutor : IFunctionExecutor + { + private readonly IFunctionActivator _functionActivator; + private Lazy _defaultExecutor; + private readonly Dictionary types = new() + { + { "MyCompany.MyHttpTriggers", Type.GetType("MyCompany.MyHttpTriggers, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")! }, + { "DependentAssemblyWithFunctions.DependencyFunction", Type.GetType("DependentAssemblyWithFunctions.DependencyFunction, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")! }, + { "MyCompany.MyProduct.MyApp.HttpFunctions", Type.GetType("MyCompany.MyProduct.MyApp.HttpFunctions, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")! }, + { "MyCompany.MyProduct.MyApp.Foo.Bar", Type.GetType("MyCompany.MyProduct.MyApp.Foo.Bar, DependentAssemblyWithFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")! } + }; + + public DirectFunctionExecutor(IFunctionActivator functionActivator) + { + _functionActivator = functionActivator ?? throw new ArgumentNullException(nameof(functionActivator)); + } + + /// + public async ValueTask ExecuteAsync(FunctionContext context) + { + var inputBindingFeature = context.Features.Get()!; + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context)!; + var inputArguments = inputBindingResult.Values; + _defaultExecutor = new Lazy(() => CreateDefaultExecutorInstance(context)); + + if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyHttpTriggers.Foo", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyHttpTriggers"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.DependencyFunction.Run", StringComparison.Ordinal)) + { + var instanceType = types["DependentAssemblyWithFunctions.DependencyFunction"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::DependentAssemblyWithFunctions.DependencyFunction; + context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.InternalFunction.Run", StringComparison.Ordinal)) + { + await _defaultExecutor.Value.ExecuteAsync(context); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.StaticFunction.Run", StringComparison.Ordinal)) + { + context.GetInvocationResult().Value = global::DependentAssemblyWithFunctions.StaticFunction.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyProduct.MyApp.HttpFunctions.Run", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyProduct.MyApp.HttpFunctions"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyProduct.MyApp.HttpFunctions; + context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + } + else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyProduct.MyApp.Foo.Bar.Run", StringComparison.Ordinal)) + { + var instanceType = types["MyCompany.MyProduct.MyApp.Foo.Bar"]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyProduct.MyApp.Foo.Bar; + context.GetInvocationResult().Value = i.Run((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + } + } + + private IFunctionExecutor CreateDefaultExecutorInstance(FunctionContext context) + { + var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.16.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; + var defaultExecutorType = Type.GetType(defaultExecutorFullName); + + return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as IFunctionExecutor; + } + } + {{GetExpectedExtensionMethodCode()}} + } + """.Replace("'", "\""); await TestHelpers.RunTestAsync( _referencedAssemblies, inputSourceCode, Constants.FileNames.GeneratedFunctionExecutor, - expectedOutput); + expected); } } } From 2d912822338bcad15ce87c3fdbf97407f3244d4e Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Tue, 28 Nov 2023 07:21:30 -0800 Subject: [PATCH 09/10] PR feedback fixes --- .../FunctionExecutor/FunctionExecutorGenerator.Emitter.cs | 6 ++++-- sdk/Sdk.Generators/FunctionMethodVisibility.cs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs index 4422a85dc..5058fbcb4 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs @@ -13,6 +13,8 @@ public partial class FunctionExecutorGenerator { internal static class Emitter { + private const string WorkerCoreAssemblyName = "Microsoft.Azure.Functions.Worker.Core"; + internal static string Emit(GeneratorExecutionContext context, IEnumerable executableFunctions, bool includeAutoRegistrationCode) { var functions = executableFunctions.ToList(); @@ -71,7 +73,7 @@ public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder private static string EmitCreateDefaultExecutorMethod(GeneratorExecutionContext context) { - var workerCoreAssembly = context.Compilation.SourceModule.ReferencedAssemblySymbols.Single(a => a.Name == "Microsoft.Azure.Functions.Worker.Core"); + var workerCoreAssembly = context.Compilation.SourceModule.ReferencedAssemblySymbols.Single(a => a.Name == WorkerCoreAssemblyName); var assemblyIdentity = workerCoreAssembly.Identity; return $$""" @@ -88,7 +90,7 @@ private IFunctionExecutor CreateDefaultExecutorInstance(FunctionContext context) private static string GetTypesDictionary(IEnumerable functions) { - // Build a dictionary of type names and it's full qualified names (including assembly identity) + // Build a dictionary of type names and its full qualified names (including assembly identity) var typesDict = functions .Where(f => !f.IsStatic) .GroupBy(f => f.ParentFunctionClassName) diff --git a/sdk/Sdk.Generators/FunctionMethodVisibility.cs b/sdk/Sdk.Generators/FunctionMethodVisibility.cs index ec4f15b68..d3163519f 100644 --- a/sdk/Sdk.Generators/FunctionMethodVisibility.cs +++ b/sdk/Sdk.Generators/FunctionMethodVisibility.cs @@ -4,7 +4,7 @@ namespace Microsoft.Azure.Functions.Worker.Sdk.Generators { /// - /// Represents the visibility of a "azure function" method and it's parent classes. + /// Represents the visibility of an "azure function" method and its parent classes. /// internal enum FunctionMethodVisibility { @@ -14,7 +14,7 @@ internal enum FunctionMethodVisibility PublicAndVisible, /// - /// The method is public, but one or more of it's parent classes are not public. + /// The method is public, but one or more of its parent classes are not public. /// PublicButContainingTypeNotVisible, From eceb5f9aabff17205230c8a7d2d5a6314e0990d8 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Tue, 28 Nov 2023 16:30:38 -0800 Subject: [PATCH 10/10] PR feedback fixes --- .../IMethodSymbolExtensions.cs} | 16 ++++++++-------- .../FunctionExecutorGenerator.Emitter.cs | 2 +- .../FunctionExecutorGenerator.Parser.cs | 2 +- .../FunctionExecutorGenerator.cs | 1 + sdk/Sdk.Generators/FunctionMethodVisibility.cs | 4 ++-- 5 files changed, 13 insertions(+), 12 deletions(-) rename sdk/Sdk.Generators/{MethodVisibilityChecker.cs => Extensions/IMethodSymbolExtensions.cs} (72%) diff --git a/sdk/Sdk.Generators/MethodVisibilityChecker.cs b/sdk/Sdk.Generators/Extensions/IMethodSymbolExtensions.cs similarity index 72% rename from sdk/Sdk.Generators/MethodVisibilityChecker.cs rename to sdk/Sdk.Generators/Extensions/IMethodSymbolExtensions.cs index b14018810..f85aae1a7 100644 --- a/sdk/Sdk.Generators/MethodVisibilityChecker.cs +++ b/sdk/Sdk.Generators/Extensions/IMethodSymbolExtensions.cs @@ -5,18 +5,18 @@ namespace Microsoft.Azure.Functions.Worker.Sdk.Generators { - internal static class MethodVisibilityChecker + internal static class IMethodSymbolExtensions { /// - /// Determines the visibility of a method. - /// The visibility of a method is determined by the following rules: - /// 1. If the method is public, and all containing types are public, return PublicAndVisible + /// Determines the visibility of an azure function method. + /// The visibility is determined by the following rules: + /// 1. If the method is public, and all containing types are public, return Public /// 2. If the method is public, but one or more containing types are not public, return PublicButContainingTypeNotVisible /// 3. If the method is not public, return NotPublic /// - /// - /// - internal static FunctionMethodVisibility GetVisibility(IMethodSymbol methodSymbol) + /// The instance representing an azure function method. + /// + internal static FunctionMethodVisibility GetVisibility(this IMethodSymbol methodSymbol) { // Check if the symbol itself is public if (methodSymbol.DeclaredAccessibility == Accessibility.Public) @@ -33,7 +33,7 @@ internal static FunctionMethodVisibility GetVisibility(IMethodSymbol methodSymbo } // If both the symbol and all containing types are public, return PublicAndVisible - return FunctionMethodVisibility.PublicAndVisible; + return FunctionMethodVisibility.Public; } // If the symbol itself is not public, return NotPublic diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs index 5058fbcb4..0fcdff7bd 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs @@ -153,7 +153,7 @@ private static string GetMethodBody(IEnumerable functions, b foreach (ExecutableFunction function in functions) { - var fast = function.Visibility == FunctionMethodVisibility.PublicAndVisible; + var fast = function.Visibility == FunctionMethodVisibility.Public; sb.Append($$""" {{(first ? string.Empty : "else ")}}if (string.Equals(context.FunctionDefinition.EntryPoint, "{{function.EntryPoint}}", StringComparison.Ordinal)) diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs index 42d68bfc1..fd6c570fc 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs @@ -52,7 +52,7 @@ internal ICollection GetFunctions(IEnumerable IsStatic = method.IsStatic, ParentFunctionClassName = defaultFormatClassName, ParentFunctionFullyQualifiedClassName = fullyQualifiedClassName, - Visibility = MethodVisibilityChecker.GetVisibility(method), + Visibility = method.GetVisibility(), AssemblyIdentity = method.ContainingAssembly.Identity.GetDisplayName(), }; diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs index c822848f8..1f239e3b5 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.cs @@ -72,6 +72,7 @@ private static IEnumerable GetDependentAssemblyFunctionsSymbols(G return visitor.FunctionMethods; } + private static bool ShouldIncludeAutoGeneratedAttributes(GeneratorExecutionContext context) { if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue( diff --git a/sdk/Sdk.Generators/FunctionMethodVisibility.cs b/sdk/Sdk.Generators/FunctionMethodVisibility.cs index d3163519f..558ce5054 100644 --- a/sdk/Sdk.Generators/FunctionMethodVisibility.cs +++ b/sdk/Sdk.Generators/FunctionMethodVisibility.cs @@ -9,9 +9,9 @@ namespace Microsoft.Azure.Functions.Worker.Sdk.Generators internal enum FunctionMethodVisibility { /// - /// The method and it's parent classes are public. + /// The method and it's parent classes are public & visible. /// - PublicAndVisible, + Public, /// /// The method is public, but one or more of its parent classes are not public.