Skip to content

Commit

Permalink
Updating FunctionExecutorGenerator to avoid long if/else chains.
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiocav committed Oct 7, 2024
1 parent 903be9c commit a98b271
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -143,26 +143,24 @@ private static string GetMethodBody(IEnumerable<ExecutableFunction> functions, b
{
var sb = new StringBuilder();
sb.Append(
$$"""
$"""
var inputBindingFeature = context.Features.Get<global::Microsoft.Azure.Functions.Worker.Context.Features.IFunctionInputBindingFeature>();
var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context);
var inputArguments = inputBindingResult.Values;
{{(anyDefaultExecutor ? $" _defaultExecutor = new Lazy<global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor>(() => CreateDefaultExecutorInstance(context));{Environment.NewLine}" : string.Empty)}}
{(anyDefaultExecutor ? $" _defaultExecutor = new Lazy<global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor>(() => CreateDefaultExecutorInstance(context));{Environment.NewLine}" : string.Empty)}
""");

bool first = true;

foreach (ExecutableFunction function in functions)
{
var fast = function.Visibility == FunctionMethodVisibility.Public;
sb.Append($$"""
{{(first ? string.Empty : "else ")}}if (string.Equals(context.FunctionDefinition.EntryPoint, "{{function.EntryPoint}}", StringComparison.Ordinal))
if (string.Equals(context.FunctionDefinition.EntryPoint, "{{function.EntryPoint}}", StringComparison.Ordinal))
{
{{(fast ? EmitFastPath(function) : EmitSlowPath())}}
return;
}
""");
first = false;
}

return sb.ToString();
Expand All @@ -171,51 +169,53 @@ private static string GetMethodBody(IEnumerable<ExecutableFunction> functions, b
private static string EmitFastPath(ExecutableFunction function)
{
var sb = new StringBuilder();
int functionParamCounter = 0;
var functionParamList = new List<string>();
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}};
""");
sb.Append($"""
var instanceType = types["{function.ParentFunctionClassName}"];
var i = _functionActivator.CreateInstance(instanceType, context) as {function.ParentFunctionFullyQualifiedClassName};
""");
}

if (!function.IsStatic)
{
sb.Append(@"
");
}
else
{
sb.Append(" ");
}
sb.Append(!function.IsStatic
? """
"""
: " ");

if (function.IsReturnValueAssignable)
{
sb.Append("context.GetInvocationResult().Value = ");
}

if (function.ShouldAwait)
{
sb.Append("await ");
}

// Parameters
int functionParamCounter = 0;
var functionParamList = new List<string>();

foreach (var argumentTypeName in function.ParameterTypeNames)
{
functionParamList.Add($"({argumentTypeName})inputArguments[{functionParamCounter++}]");
}

var methodParamsStr = string.Join(", ", functionParamList);

sb.Append(function.IsStatic
? $"{function.ParentFunctionFullyQualifiedClassName}.{function.MethodName}({methodParamsStr});"
: $"i.{function.MethodName}({methodParamsStr});");

return sb.ToString();
}

private static string EmitSlowPath()
{
return
" await _defaultExecutor.Value.ExecuteAsync(context);";
return " await _defaultExecutor.Value.ExecuteAsync(context);";
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/Sdk.Generators/Sdk.Generators.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<IncludeBuildOutput>false</IncludeBuildOutput>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<MinorProductVersion>3</MinorProductVersion>
<PatchProductVersion>2</PatchProductVersion>
<PatchProductVersion>3</PatchProductVersion>
<IsRoslynComponent>true</IsRoslynComponent>
</PropertyGroup>

Expand Down
2 changes: 1 addition & 1 deletion sdk/Sdk/Sdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<MinorProductVersion>18</MinorProductVersion>
<PatchProductVersion>0</PatchProductVersion>
<PatchProductVersion>1</PatchProductVersion>
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
<PackageId>Microsoft.Azure.Functions.Worker.Sdk</PackageId>
<Description>This package provides development time support for the Azure Functions .NET Worker.</Description>
Expand Down
12 changes: 5 additions & 7 deletions sdk/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
- My change description (#PR/#issue)
-->

### Microsoft.Azure.Functions.Worker.Sdk 1.18.0
### Microsoft.Azure.Functions.Worker.Sdk 1.18.1

- Fix incorrect function version in build message (#2606)
- Fix inner build failures when central package management is enabled (#2689)
- Add support to publish a Function App (Flex Consumption) with `ZipDeploy` (#2712)
- Add `'UseBlobContainerDeploy'` property to identify when to use `OneDeploy` publish API endpoint (`"<publish_url>/api/publish"`)
- Enhance `ZipDeploy` deployment status logging by appending the `'status_message'` (when defined) to the output messages
- Updated `Microsoft.Azure.Functions.Worker.Sdk.Generators` reference to 1.3.3.

### Microsoft.Azure.Functions.Worker.Sdk.Generators <version>
### Microsoft.Azure.Functions.Worker.Sdk.Generators 1.3.3

- Changed `FunctionExecutorGenerator` to avoid generation of long `if`/`else` chains for apps with a large number of functions.

- <entry>
26 changes: 20 additions & 6 deletions test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,38 +101,52 @@ public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunction
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]);
goto end;
}
else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.DependencyFunction.Run", StringComparison.Ordinal))
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]);
goto end;
}
else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.InternalFunction.Run", StringComparison.Ordinal))
if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.InternalFunction.Run", StringComparison.Ordinal))
{
await _defaultExecutor.Value.ExecuteAsync(context);
goto end;
}
else if (string.Equals(context.FunctionDefinition.EntryPoint, "DependentAssemblyWithFunctions.StaticFunction.Run", StringComparison.Ordinal))
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]);
goto end;
}
else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyProduct.MyApp.HttpFunctions.Run", StringComparison.Ordinal))
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]);
goto end;
}
else if (string.Equals(context.FunctionDefinition.EntryPoint, "MyCompany.MyProduct.MyApp.Foo.Bar.Run", StringComparison.Ordinal))
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]);
goto end;
}
end:
return;
}
private global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor CreateDefaultExecutorInstance(global::Microsoft.Azure.Functions.Worker.FunctionContext context)
{
var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.18.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c";
var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=1.19.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c";
var defaultExecutorType = global::System.Type.GetType(defaultExecutorFullName);
return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor;
Expand Down
Loading

0 comments on commit a98b271

Please sign in to comment.