Skip to content

Commit 9c3380d

Browse files
authored
Add source generated function metadata (#956)
Implementation change of function-indexing for cold-start improvements. Introduces support for source-generating function metadata.
1 parent 28ae2dd commit 9c3380d

31 files changed

+3697
-105
lines changed

release_notes.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,25 @@
44
-->
55

66
- Added support for surfacing user-thrown exception to App Insights (#939)
7-
- Add new analyzer rules for the source-generated function metadata preview (#1048)
8-
- Bump gRPC dependencies to latest version (#1085)
7+
- Source-generated function metadata: implementation change to improve cold-start performance (#956)
8+
9+
Steps for opting into the preview:
10+
11+
1. Add MSBuild property `<FunctionsEnableWorkerIndexing>true</FunctionsEnableWorkerIndexing>` app's `.csproj` file.
12+
2. In `local.settings.json` add the property `"AzureWebJobsFeatureFlags": "EnableWorkerIndexing"` to configure the Azure Functions host to use worker-indexing.
13+
3. Call the `IHostBuilder` extension, `ConfigureGeneratedFunctionMetadataProvider` in `Program.cs`:
14+
15+
```
16+
class Program
17+
{
18+
static async Task Main(string[] args)
19+
{
20+
var host = new HostBuilder()
21+
.ConfigureFunctionsWorkerDefaults()
22+
.ConfigureGeneratedFunctionMetadataProvider()
23+
.Build();
24+
25+
await host.RunAsync();
26+
}
27+
}
28+
```
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.Azure.Functions.Worker.Sdk.Generators
7+
{
8+
internal class GeneratorFunctionMetadata
9+
{
10+
public string? Name { get; set; }
11+
12+
public string? ScriptFile { get; set; }
13+
14+
public string? FunctionId { get; set; }
15+
16+
public bool IsProxy { get; set; }
17+
18+
public string? EntryPoint { get; set; }
19+
20+
public string? Language { get; set; }
21+
22+
public bool IsHttpTrigger { get; set; }
23+
24+
public IDictionary<string, object> Properties { get; set; } = new Dictionary<string, object>();
25+
26+
public IList<IDictionary<string, string>> RawBindings { get; set; } = new List<IDictionary<string, string>>(); // List of <propertyName, propertyValue> bindings.
27+
}
28+
}

sdk/Sdk.Generators/Constants.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.Functions.Worker.Sdk.Generators
5+
{
6+
internal static class Constants
7+
{
8+
// Our types
9+
internal const string BindingAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.BindingAttribute";
10+
internal const string OutputBindingAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.OutputBindingAttribute";
11+
internal const string FunctionNameType = "Microsoft.Azure.Functions.Worker.FunctionAttribute";
12+
internal const string HttpResponseType = "Microsoft.Azure.Functions.Worker.Http.HttpResponseData";
13+
internal const string EventHubsTriggerType = "Microsoft.Azure.Functions.Worker.EventHubTriggerAttribute";
14+
15+
// System types
16+
internal const string IEnumerableType = "System.Collections.IEnumerable";
17+
internal const string IEnumerableGenericType = "System.Collections.Generic.IEnumerable`1";
18+
internal const string IEnumerableOfStringType = "System.Collections.Generic.IEnumerable`1<System.String>";
19+
internal const string IEnumerableOfBinaryType = "System.Collections.Generic.IEnumerable`1<System.Byte[]>";
20+
internal const string IEnumerableOfT = "System.Collections.Generic.IEnumerable`1<T>";
21+
internal const string IEnumerableOfKeyValuePair = "System.Collections.Generic.IEnumerable`1<System.Collections.Generic.KeyValuePair`2<TKey,TValue>>";
22+
internal const string StringType = "System.String";
23+
internal const string ByteArrayType = "System.Byte[]";
24+
internal const string ByteStructType = "System.Byte";
25+
internal const string TaskGenericType = "System.Threading.Tasks.Task`1";
26+
internal const string TaskType = "System.Threading.Tasks.Task";
27+
internal const string VoidType = "System.Void";
28+
internal const string ReadOnlyMemoryOfBytes = "System.ReadOnlyMemory`1<System.Byte>";
29+
internal const string LookupGenericType = "System.Linq.Lookup`2";
30+
internal const string DictionaryGenericType = "System.Collections.Generic.Dictionary`2";
31+
32+
internal const string ReturnBindingName = "$return";
33+
internal const string HttpResponseBindingName = "HttpResponse";
34+
internal const string HttpTriggerBindingType = "Microsoft.Azure.Functions.Worker.HttpTriggerAttribute";
35+
internal const string IsBatchedKey = "IsBatched";
36+
}
37+
}

sdk/Sdk.Generators/DiagnosticDescriptors.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,33 @@ private static DiagnosticDescriptor Create(string id, string title, string messa
2727
messageFormat: "'{0}' class must have a public parameterless constructor.",
2828
category: "Startup",
2929
severity: DiagnosticSeverity.Error);
30+
31+
public static DiagnosticDescriptor MultipleBindingsGroupedTogether { get; }
32+
= Create(id: "AZFW0005",
33+
title: "Multiple bindings are grouped together on one property, method, or parameter syntax.",
34+
messageFormat: "'{0}' must have only one binding attribute.",
35+
category: "FunctionMetadataGeneration",
36+
severity: DiagnosticSeverity.Error);
37+
38+
public static DiagnosticDescriptor SymbolNotFound { get; }
39+
= Create(id: "AZFW0006",
40+
title: "Symbol could not be found in user compilation.",
41+
messageFormat: "The symbol '{0}' could not be found.",
42+
category: "FunctionMetadataGeneration",
43+
severity: DiagnosticSeverity.Warning);
44+
45+
public static DiagnosticDescriptor MultipleHttpResponseTypes { get; }
46+
= Create(id: "AZFW0007",
47+
title: "Symbol could not be found in user compilation.",
48+
messageFormat: "Found multiple public properties with type '{0}' defined in output type '{1}'.Only one HTTP response binding type is supported in your return type definition.",
49+
category: "FunctionMetadataGeneration",
50+
severity: DiagnosticSeverity.Error);
51+
52+
public static DiagnosticDescriptor InvalidEventHubsTrigger { get; }
53+
= Create(id: "AZFW0008",
54+
title: "EventHub Trigger invalid.",
55+
messageFormat: "The EventHub trigger on parameter '{0}' is invalid. IsBatched may be used incorrectly.",
56+
category: "FunctionMetadataGeneration",
57+
severity: DiagnosticSeverity.Error);
3058
}
3159
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.Functions.Worker.Sdk.Generators.Enums
5+
{
6+
internal enum Cardinality
7+
{
8+
Undefined,
9+
Many,
10+
One
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.Functions.Worker.Sdk.Generators
5+
{
6+
internal enum DataType
7+
{
8+
Undefined,
9+
Binary,
10+
String
11+
}
12+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using Microsoft.CodeAnalysis;
9+
10+
namespace Microsoft.Azure.Functions.Worker.Sdk.Generators
11+
{
12+
internal static class ISymbolExtensions
13+
{
14+
/// <summary>
15+
/// Walks the symbol tree to generate the fully qualified name of a type symbol.
16+
/// Ex input: A Symbol for "Task" token
17+
/// Output: "System.Threading.Tasks.Task"
18+
/// </summary>
19+
internal static string GetFullName(this ITypeSymbol typeSymbol)
20+
{
21+
var symbol = typeSymbol as ISymbol;
22+
23+
if (symbol == null || IsRootNamespace(symbol))
24+
{
25+
return string.Empty;
26+
}
27+
28+
var sb = new StringBuilder(symbol.MetadataName);
29+
30+
if (symbol is IArrayTypeSymbol arraySymbol) // arrays need to be handled differently b/c the properties used to get the full name for other symbols are null for IArrayTypeSymbols
31+
{
32+
sb.Append(arraySymbol.ElementType.GetFullName()); // ex: for string[], the ElementType is System.String and that is the full name returned at this step.
33+
sb.Append("[]"); // System.Byte[], System.String[] are the full names for array types of element type Byte, String and we auto-add the brackets here.
34+
}
35+
else
36+
{
37+
symbol = symbol.ContainingSymbol;
38+
39+
while (!IsRootNamespace(symbol))
40+
{
41+
sb.Insert(0, '.');
42+
sb.Insert(0, symbol.MetadataName);
43+
symbol = symbol.ContainingSymbol;
44+
}
45+
}
46+
47+
return sb.ToString();
48+
}
49+
50+
private static bool IsRootNamespace(ISymbol symbol)
51+
{
52+
if (symbol is INamespaceSymbol namespaceSymbol)
53+
{
54+
return namespaceSymbol.IsGlobalNamespace;
55+
}
56+
57+
return false;
58+
}
59+
60+
internal static bool IsOrDerivedFrom(this ITypeSymbol symbol, ITypeSymbol? other)
61+
{
62+
if (other is null)
63+
{
64+
return false;
65+
}
66+
67+
var current = symbol;
68+
69+
while (current != null)
70+
{
71+
if(SymbolEqualityComparer.Default.Equals(current, other) || SymbolEqualityComparer.Default.Equals(current.OriginalDefinition, other))
72+
{
73+
return true;
74+
}
75+
76+
current = current.BaseType;
77+
}
78+
79+
return false;
80+
}
81+
82+
internal static bool IsOrImplements(this ITypeSymbol symbol, ITypeSymbol? other)
83+
{
84+
if (other is null)
85+
{
86+
return false;
87+
}
88+
89+
var current = symbol;
90+
91+
while (current != null)
92+
{
93+
foreach (var member in current.Interfaces)
94+
{
95+
if (member.IsOrDerivedFrom(other))
96+
{
97+
return true;
98+
}
99+
}
100+
101+
current = current.BaseType;
102+
}
103+
104+
return false;
105+
}
106+
107+
internal static bool IsOrImplementsOrDerivesFrom(this ITypeSymbol symbol, ITypeSymbol? other)
108+
{
109+
return symbol.IsOrImplements(other) || symbol.IsOrDerivedFrom(other);
110+
}
111+
}
112+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Microsoft.Azure.Functions.Worker.Sdk.Generators
8+
{
9+
internal static class StringExtensions
10+
{
11+
public static string TrimStringFromEnd(this string str, string end)
12+
{
13+
if (end is null)
14+
{
15+
throw new ArgumentNullException(nameof(end));
16+
}
17+
18+
return str.EndsWith(end) ? str.Substring(0, str.Length - end.Length) : str;
19+
}
20+
21+
public static string TrimStringsFromEnd(this string str, IReadOnlyList<string> strings)
22+
{
23+
var result = str;
24+
25+
foreach (string s in strings)
26+
{
27+
result = result.TrimStringFromEnd(s);
28+
}
29+
30+
return result;
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)