Skip to content

Commit 8c4c2d6

Browse files
committed
Fixing issues with function target invalidation on return type changes
1 parent 1e87ddd commit 8c4c2d6

File tree

5 files changed

+118
-69
lines changed

5 files changed

+118
-69
lines changed

src/WebJobs.Script/Description/DotNet/CSharp/CSharpCompilation.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,17 @@ public FunctionSignature GetEntryPointSignature(IFunctionEntryPointResolver entr
5151
.Select(m => new MethodReference<IMethodSymbol>(m.Name, m.DeclaredAccessibility == Accessibility.Public, m));
5252

5353
IMethodSymbol entryPointReference = entryPointResolver.GetFunctionEntryPoint(methods).Value;
54-
bool hasLocalTypeReferences = entryPointReference.Parameters.Any(p => IsOrUsesAssemblyType(p.Type, entryPointReference.ContainingAssembly));
54+
bool hasLocalTypeReferences = HasLocalTypeReferences(entryPointReference);
5555
var functionParameters = entryPointReference.Parameters.Select(p => new FunctionParameter(p.Name, GetFullTypeName(p.Type), p.IsOptional, p.RefKind));
5656

57-
return new FunctionSignature(entryPointReference.ContainingType.Name, entryPointReference.Name, ImmutableArray.CreateRange(functionParameters.ToArray()), hasLocalTypeReferences);
57+
return new FunctionSignature(entryPointReference.ContainingType.Name, entryPointReference.Name,
58+
ImmutableArray.CreateRange(functionParameters.ToArray()), GetFullTypeName(entryPointReference.ReturnType), hasLocalTypeReferences);
59+
}
60+
61+
private static bool HasLocalTypeReferences(IMethodSymbol entryPointReference)
62+
{
63+
return IsOrUsesAssemblyType(entryPointReference.ReturnType, entryPointReference.ContainingAssembly)
64+
|| entryPointReference.Parameters.Any(p => IsOrUsesAssemblyType(p.Type, entryPointReference.ContainingAssembly));
5865
}
5966

6067
private static string GetFullTypeName(ITypeSymbol type)

src/WebJobs.Script/Description/DotNet/FSharp/FSharpCompilation.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ public FunctionSignature GetEntryPointSignature(IFunctionEntryPointResolver entr
6464
// For F#, we always set this to true for now.
6565
bool hasLocalTypeReference = true;
6666

67-
var signature = new FunctionSignature(entryPointReference.DeclaringType.Name, entryPointReference.Name, parameters.ToImmutableArray(), hasLocalTypeReference);
68-
67+
var signature = new FunctionSignature(entryPointReference.DeclaringType.Name, entryPointReference.Name,
68+
parameters.ToImmutableArray(), entryPointReference.ReturnType.Name, hasLocalTypeReference);
69+
6970
return signature;
7071
}
7172

src/WebJobs.Script/Description/DotNet/FunctionSignature.cs

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.Collections.Immutable;
7-
using System.Globalization;
86
using System.Linq;
9-
using System.Threading.Tasks;
107
using Microsoft.CodeAnalysis;
11-
using Microsoft.CodeAnalysis.CSharp.Syntax;
128

139
namespace Microsoft.Azure.WebJobs.Script.Description
1410
{
@@ -22,50 +18,30 @@ public sealed class FunctionSignature : IEquatable<FunctionSignature>
2218
private readonly bool _hasLocalTypeReference;
2319
private readonly string _parentTypeName;
2420
private readonly string _methodName;
21+
private readonly string _returnTypeName;
2522

26-
public FunctionSignature(string parentTypeName, string methodName, ImmutableArray<FunctionParameter> parameters, bool hasLocalTypeReference)
23+
public FunctionSignature(string parentTypeName, string methodName, ImmutableArray<FunctionParameter> parameters, string returnTypeName, bool hasLocalTypeReference)
2724
{
2825
_parameters = parameters;
2926
_hasLocalTypeReference = hasLocalTypeReference;
3027
_parentTypeName = parentTypeName;
28+
_returnTypeName = returnTypeName;
3129
_methodName = methodName;
3230
}
3331

3432
/// <summary>
3533
/// Returns true if the function uses locally defined types (i.e. types defined in the function assembly) in its parameters;
3634
/// otherwise, false.
3735
/// </summary>
38-
public bool HasLocalTypeReference
39-
{
40-
get
41-
{
42-
return _hasLocalTypeReference;
43-
}
44-
}
36+
public bool HasLocalTypeReference => _hasLocalTypeReference;
4537

46-
public ImmutableArray<FunctionParameter> Parameters
47-
{
48-
get
49-
{
50-
return _parameters;
51-
}
52-
}
38+
public ImmutableArray<FunctionParameter> Parameters => _parameters;
5339

54-
public string ParentTypeName
55-
{
56-
get
57-
{
58-
return _parentTypeName;
59-
}
60-
}
40+
public string ParentTypeName => _parentTypeName;
6141

62-
public string MethodName
63-
{
64-
get
65-
{
66-
return _methodName;
67-
}
68-
}
42+
public string MethodName => _methodName;
43+
44+
public string ReturnTypeName => _returnTypeName;
6945

7046
public bool Equals(FunctionSignature other)
7147
{
@@ -74,7 +50,11 @@ public bool Equals(FunctionSignature other)
7450
return false;
7551
}
7652

77-
if (_parameters.Count() != other._parameters.Count())
53+
if (!string.Equals(ParentTypeName, other.ParentTypeName) ||
54+
!string.Equals(MethodName, other.MethodName) ||
55+
!string.Equals(ReturnTypeName, other.ReturnTypeName) ||
56+
HasLocalTypeReference != other.HasLocalTypeReference ||
57+
_parameters.Count() != other._parameters.Count())
7858
{
7959
return false;
8060
}
@@ -84,6 +64,12 @@ public bool Equals(FunctionSignature other)
8464

8565
public override bool Equals(object obj) => Equals(obj as FunctionSignature);
8666

87-
public override int GetHashCode() => _parameters.Aggregate(0, (a, p) => a ^ p.GetHashCode());
67+
public override int GetHashCode() => GetPropertyHashCode(ParentTypeName) ^
68+
GetPropertyHashCode(MethodName) ^
69+
GetPropertyHashCode(ReturnTypeName) ^
70+
HasLocalTypeReference.GetHashCode() ^
71+
_parameters.Aggregate(0, (a, p) => a ^ p.GetHashCode());
72+
73+
private static int GetPropertyHashCode(string value) => value?.GetHashCode() ?? 0;
8874
}
8975
}

test/WebJobs.Script.Tests/CSharpFunctionSignatureTests.cs

Lines changed: 84 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Linq;
56
using Microsoft.Azure.WebJobs.Script.Description;
67
using Microsoft.CodeAnalysis;
@@ -14,7 +15,7 @@ public class CSharpFunctionSignatureTests
1415
[Fact]
1516
public void Matches_IsTrue_WhenParametersAreEqual()
1617
{
17-
var function1 = @"using System;
18+
var function1 = @"using System;
1819
public static void Run(string id, out string output)
1920
{
2021
output = string.Empty;
@@ -27,22 +28,33 @@ public static void Run(string id, out string output)
2728
output = result;
2829
}";
2930

30-
var tree1 = CSharpSyntaxTree.ParseText(function1, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));
31-
var tree2 = CSharpSyntaxTree.ParseText(function2, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));
31+
Tuple<FunctionSignature, FunctionSignature> signatures = GetFunctionSignatures(function1, function2);
3232

33-
var references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(string).Assembly.Location) };
33+
Assert.True(signatures.Item1.Equals(signatures.Item2));
34+
Assert.Equal(signatures.Item1.GetHashCode(), signatures.Item2.GetHashCode());
35+
}
3436

35-
var compilation1 = new Script.Description.CSharpCompilation(CodeAnalysis.CSharp.CSharpCompilation.Create("test1", references: references)
36-
.AddSyntaxTrees(tree1));
37+
[Fact]
38+
public void Matches_IsFalse_WhenReturnTypesAreDifferent()
39+
{
40+
var function1 = @"using System;
41+
public static string Run(string id, out string output)
42+
{
43+
output = string.Empty;
44+
return string.Empty;
45+
}";
3746

38-
var compilation2 = new Script.Description.CSharpCompilation(CodeAnalysis.CSharp.CSharpCompilation.Create("test2", references: references)
39-
.AddSyntaxTrees(tree2));
47+
var function2 = @"using System;
48+
public static void Run(string id, out string output)
49+
{
50+
string result = string.Empty;
51+
output = result;
52+
}";
4053

41-
var signature1 = compilation1.GetEntryPointSignature(new FunctionEntryPointResolver());
42-
var signature2 = compilation2.GetEntryPointSignature(new FunctionEntryPointResolver());
54+
Tuple<FunctionSignature, FunctionSignature> signatures = GetFunctionSignatures(function1, function2);
4355

44-
Assert.True(signature1.Equals(signature2));
45-
Assert.Equal(signature1.GetHashCode(), signature2.GetHashCode());
56+
Assert.False(signatures.Item1.Equals(signatures.Item2));
57+
Assert.NotEqual(signatures.Item1.GetHashCode(), signatures.Item2.GetHashCode());
4658
}
4759

4860
[Fact]
@@ -61,6 +73,14 @@ public static void Run(string id, out string output)
6173
output = result;
6274
}";
6375

76+
Tuple<FunctionSignature, FunctionSignature> signatures = GetFunctionSignatures(function1, function2);
77+
78+
Assert.False(signatures.Item1.Equals(signatures.Item2));
79+
Assert.NotEqual(signatures.Item1.GetHashCode(), signatures.Item2.GetHashCode());
80+
}
81+
82+
private Tuple<FunctionSignature, FunctionSignature> GetFunctionSignatures(string function1, string function2)
83+
{
6484
var tree1 = CSharpSyntaxTree.ParseText(function1, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));
6585
var tree2 = CSharpSyntaxTree.ParseText(function2, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));
6686

@@ -75,8 +95,7 @@ public static void Run(string id, out string output)
7595
var signature1 = compilation1.GetEntryPointSignature(new FunctionEntryPointResolver());
7696
var signature2 = compilation2.GetEntryPointSignature(new FunctionEntryPointResolver());
7797

78-
Assert.False(signature1.Equals(signature2));
79-
Assert.NotEqual(signature1.GetHashCode(), signature2.GetHashCode());
98+
return Tuple.Create(signature1, signature2);
8099
}
81100

82101
[Fact]
@@ -97,22 +116,10 @@ public static void Run( System.String id ,
97116
output = result;
98117
}";
99118

100-
var tree1 = CSharpSyntaxTree.ParseText(function1, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));
101-
var tree2 = CSharpSyntaxTree.ParseText(function2, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));
102-
103-
var references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(string).Assembly.Location) };
104-
105-
var compilation1 = new Script.Description.CSharpCompilation(CodeAnalysis.CSharp.CSharpCompilation.Create("test1", references: references)
106-
.AddSyntaxTrees(tree1));
107-
108-
var compilation2 = new Script.Description.CSharpCompilation(CodeAnalysis.CSharp.CSharpCompilation.Create("test2", references: references)
109-
.AddSyntaxTrees(tree2));
119+
Tuple<FunctionSignature, FunctionSignature> signatures = GetFunctionSignatures(function1, function2);
110120

111-
var signature1 = compilation1.GetEntryPointSignature(new FunctionEntryPointResolver());
112-
var signature2 = compilation2.GetEntryPointSignature(new FunctionEntryPointResolver());
113-
114-
Assert.True(signature1.Equals(signature2));
115-
Assert.Equal(signature1.GetHashCode(), signature2.GetHashCode());
121+
Assert.True(signatures.Item1.Equals(signatures.Item2));
122+
Assert.Equal(signatures.Item1.GetHashCode(), signatures.Item2.GetHashCode());
116123
}
117124

118125
[Fact]
@@ -148,6 +155,54 @@ public static void Run(string id, ICollection<Test> test1)
148155
149156
}
150157
158+
public class Test
159+
{
160+
public string Id { get; set; }
161+
}";
162+
163+
var tree = CSharpSyntaxTree.ParseText(function1, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));
164+
var references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(string).Assembly.Location) };
165+
var compilation = CodeAnalysis.CSharp.CSharpCompilation.Create("test1", references: references).AddSyntaxTrees(tree);
166+
167+
var signature1 = new Script.Description.CSharpCompilation(compilation).GetEntryPointSignature(new FunctionEntryPointResolver());
168+
169+
Assert.True(signature1.HasLocalTypeReference);
170+
}
171+
172+
[Fact]
173+
public void Matches_IsTrue_WhenUsingLocalTypesInReturn()
174+
{
175+
var function1 = @"using System;
176+
using System.Collections.Generic;
177+
public static Test Run(string id)
178+
{
179+
return null;
180+
}
181+
182+
public class Test
183+
{
184+
public string Id { get; set; }
185+
}";
186+
187+
var tree = CSharpSyntaxTree.ParseText(function1, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script));
188+
var references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(string).Assembly.Location) };
189+
var compilation = CodeAnalysis.CSharp.CSharpCompilation.Create("test1", references: references).AddSyntaxTrees(tree);
190+
191+
var signature1 = new Script.Description.CSharpCompilation(compilation).GetEntryPointSignature(new FunctionEntryPointResolver());
192+
193+
Assert.True(signature1.HasLocalTypeReference);
194+
}
195+
196+
[Fact]
197+
public void Matches_IsTrue_WhenUsingLocalTypesInGenericReturn()
198+
{
199+
var function1 = @"using System;
200+
using System.Collections.Generic;
201+
public static List<Test> Run(string id)
202+
{
203+
return null;
204+
}
205+
151206
public class Test
152207
{
153208
public string Id { get; set; }

test/WebJobs.Script.Tests/Description/DotNet/DotNetFunctionInvokerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public void ValidateFunctionBindingArguments_ReturnBinding_Succeeds()
150150
{
151151
new FunctionParameter("input", "String", false, RefKind.None)
152152
};
153-
FunctionSignature signature = new FunctionSignature("Test", "Test", ImmutableArray.CreateRange<FunctionParameter>(parameters), false);
153+
FunctionSignature signature = new FunctionSignature("Test", "Test", ImmutableArray.CreateRange<FunctionParameter>(parameters), "Test", false);
154154

155155
Collection<FunctionBinding> inputBindings = new Collection<FunctionBinding>()
156156
{

0 commit comments

Comments
 (0)