Skip to content

Commit 3920563

Browse files
Remove ItemCollection from TagHelperDescriptorProviderContext (#10720)
Every `TagHelperDescriptorProviderContext` creates an `ItemCollection` to hold onto, at most, two objects: a `Compilation` and a target `ISymbol`. This is enormously wasteful because an `ItemCollection` internally creates a `ConcurrentDictionary<object, object>`. So, I've changed `TagHelperDescriptorProviderContext` to just hold onto a compilation and a target symbol and avoid the `ItemCollection` altogether. I recommend reviewing commit-by-commit. Also, I bumped into a long standing compiler bug in `EventHandlerTagHelperDescriptorProvider` that was recently filed by a customer: #10497. I opted to stay on target and not fix this issue, but I made it painfully obvious where the fix would go. 😄
2 parents 9044af2 + 9f6c7b6 commit 3920563

File tree

37 files changed

+520
-865
lines changed

37 files changed

+520
-865
lines changed

src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ViewComponentTagHelperDescriptorProviderTest.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.AspNetCore.Razor.Language;
77
using Microsoft.CodeAnalysis;
88
using Microsoft.CodeAnalysis.CSharp;
9-
using Microsoft.CodeAnalysis.Razor;
109
using Xunit;
1110
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
1211

@@ -28,8 +27,7 @@ public class StringParameterViewComponent
2827

2928
var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code));
3029

31-
var context = TagHelperDescriptorProviderContext.Create();
32-
context.SetCompilation(compilation);
30+
var context = new TagHelperDescriptorProviderContext(compilation);
3331

3432
var provider = new ViewComponentTagHelperDescriptorProvider()
3533
{

src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ViewComponentTagHelperDescriptorProviderTest.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.AspNetCore.Razor.Language;
77
using Microsoft.CodeAnalysis;
88
using Microsoft.CodeAnalysis.CSharp;
9-
using Microsoft.CodeAnalysis.Razor;
109
using Xunit;
1110
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
1211

@@ -28,8 +27,7 @@ public class StringParameterViewComponent
2827

2928
var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code));
3029

31-
var context = TagHelperDescriptorProviderContext.Create();
32-
context.SetCompilation(compilation);
30+
var context = new TagHelperDescriptorProviderContext(compilation);
3331

3432
var provider = new ViewComponentTagHelperDescriptorProvider()
3533
{

src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ViewComponentTagHelperDescriptorProviderTest.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.AspNetCore.Razor.Language;
77
using Microsoft.CodeAnalysis;
88
using Microsoft.CodeAnalysis.CSharp;
9-
using Microsoft.CodeAnalysis.Razor;
109
using Xunit;
1110
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
1211

@@ -28,8 +27,7 @@ public class StringParameterViewComponent
2827

2928
var compilation = MvcShim.BaseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code));
3029

31-
var context = TagHelperDescriptorProviderContext.Create();
32-
context.SetCompilation(compilation);
30+
var context = new TagHelperDescriptorProviderContext(compilation);
3331

3432
var provider = new ViewComponentTagHelperDescriptorProvider()
3533
{

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs

Lines changed: 88 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,22 @@
55
using System.Collections.Generic;
66
using System.Globalization;
77
using System.Linq;
8+
using Microsoft.AspNetCore.Razor;
89
using Microsoft.AspNetCore.Razor.Language;
910
using Microsoft.AspNetCore.Razor.Language.Components;
1011
using Microsoft.AspNetCore.Razor.PooledObjects;
1112
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
1213

1314
namespace Microsoft.CodeAnalysis.Razor;
1415

15-
internal class BindTagHelperDescriptorProvider : ITagHelperDescriptorProvider
16+
// Run after the component tag helper provider, because we need to see the results.
17+
internal sealed class BindTagHelperDescriptorProvider() : TagHelperDescriptorProviderBase(order: 1000)
1618
{
17-
private static TagHelperDescriptor? s_fallbackBindTagHelper;
19+
private static readonly Lazy<TagHelperDescriptor> s_fallbackBindTagHelper = new(CreateFallbackBindTagHelper);
1820

19-
// Run after the component tag helper provider, because we need to see the results.
20-
public int Order { get; set; } = 1000;
21-
22-
public RazorEngine? Engine { get; set; }
23-
24-
public void Execute(TagHelperDescriptorProviderContext context)
21+
public override void Execute(TagHelperDescriptorProviderContext context)
2522
{
26-
if (context == null)
27-
{
28-
throw new ArgumentNullException(nameof(context));
29-
}
23+
ArgHelper.ThrowIfNull(context);
3024

3125
// This provider returns tag helper information for 'bind' which doesn't necessarily
3226
// map to any real component. Bind behaviors more like a macro, which can map a single LValue to
@@ -91,11 +85,7 @@ public void Execute(TagHelperDescriptorProviderContext context)
9185
// we have. Case #4 is data-driven based on component definitions.
9286
//
9387
// We provide a good set of attributes that map to the HTML dom. This set is user extensible.
94-
var compilation = context.GetCompilation();
95-
if (compilation == null)
96-
{
97-
return;
98-
}
88+
var compilation = context.Compilation;
9989

10090
var bindMethods = compilation.GetTypeByMetadataName(ComponentsApi.BindConverter.FullTypeName);
10191
if (bindMethods == null)
@@ -105,14 +95,13 @@ public void Execute(TagHelperDescriptorProviderContext context)
10595
return;
10696
}
10797

108-
var targetSymbol = context.Items.GetTargetSymbol();
109-
if (targetSymbol is not null && !SymbolEqualityComparer.Default.Equals(targetSymbol, bindMethods.ContainingAssembly))
98+
if (context.TargetSymbol is { } targetSymbol && !SymbolEqualityComparer.Default.Equals(targetSymbol, bindMethods.ContainingAssembly))
11099
{
111100
return;
112101
}
113102

114103
// Tag Helper definition for case #1. This is the most general case.
115-
context.Results.Add(GetOrCreateFallbackBindTagHelper());
104+
context.Results.Add(s_fallbackBindTagHelper.Value);
116105

117106
var bindElementAttribute = compilation.GetTypeByMetadataName(ComponentsApi.BindElementAttribute.FullTypeName);
118107
var bindInputElementAttribute = compilation.GetTypeByMetadataName(ComponentsApi.BindInputElementAttribute.FullTypeName);
@@ -129,112 +118,107 @@ public void Execute(TagHelperDescriptorProviderContext context)
129118
collector.Collect(context);
130119
}
131120

132-
private static TagHelperDescriptor GetOrCreateFallbackBindTagHelper()
121+
private static TagHelperDescriptor CreateFallbackBindTagHelper()
133122
{
134-
return s_fallbackBindTagHelper ??= CreateFallbackBindTagHelper();
135-
136-
static TagHelperDescriptor CreateFallbackBindTagHelper()
123+
using var _ = TagHelperDescriptorBuilder.GetPooledInstance(
124+
ComponentMetadata.Bind.TagHelperKind, "Bind", ComponentsApi.AssemblyName,
125+
out var builder);
126+
127+
builder.CaseSensitive = true;
128+
builder.SetDocumentation(DocumentationDescriptor.BindTagHelper_Fallback);
129+
130+
builder.SetMetadata(
131+
SpecialKind(ComponentMetadata.Bind.TagHelperKind),
132+
MakeTrue(TagHelperMetadata.Common.ClassifyAttributesOnly),
133+
RuntimeName(ComponentMetadata.Bind.RuntimeName),
134+
MakeTrue(ComponentMetadata.Bind.FallbackKey),
135+
TypeName("Microsoft.AspNetCore.Components.Bind"),
136+
TypeNamespace("Microsoft.AspNetCore.Components"),
137+
TypeNameIdentifier("Bind"));
138+
139+
builder.TagMatchingRule(rule =>
137140
{
138-
using var _ = TagHelperDescriptorBuilder.GetPooledInstance(
139-
ComponentMetadata.Bind.TagHelperKind, "Bind", ComponentsApi.AssemblyName,
140-
out var builder);
141-
142-
builder.CaseSensitive = true;
143-
builder.SetDocumentation(DocumentationDescriptor.BindTagHelper_Fallback);
144-
145-
builder.SetMetadata(
146-
SpecialKind(ComponentMetadata.Bind.TagHelperKind),
147-
MakeTrue(TagHelperMetadata.Common.ClassifyAttributesOnly),
148-
RuntimeName(ComponentMetadata.Bind.RuntimeName),
149-
MakeTrue(ComponentMetadata.Bind.FallbackKey),
150-
TypeName("Microsoft.AspNetCore.Components.Bind"),
151-
TypeNamespace("Microsoft.AspNetCore.Components"),
152-
TypeNameIdentifier("Bind"));
153-
154-
builder.TagMatchingRule(rule =>
141+
rule.TagName = "*";
142+
rule.Attribute(attribute =>
155143
{
156-
rule.TagName = "*";
157-
rule.Attribute(attribute =>
158-
{
159-
attribute.Name = "@bind-";
160-
attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch;
161-
attribute.SetMetadata(Attributes.IsDirectiveAttribute);
162-
});
144+
attribute.Name = "@bind-";
145+
attribute.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch;
146+
attribute.SetMetadata(Attributes.IsDirectiveAttribute);
163147
});
148+
});
164149

165-
builder.BindAttribute(attribute =>
166-
{
167-
attribute.SetDocumentation(DocumentationDescriptor.BindTagHelper_Fallback);
150+
builder.BindAttribute(attribute =>
151+
{
152+
attribute.SetDocumentation(DocumentationDescriptor.BindTagHelper_Fallback);
168153

169-
var attributeName = "@bind-...";
170-
attribute.Name = attributeName;
171-
attribute.AsDictionary("@bind-", typeof(object).FullName);
154+
var attributeName = "@bind-...";
155+
attribute.Name = attributeName;
156+
attribute.AsDictionary("@bind-", typeof(object).FullName);
172157

173-
attribute.SetMetadata(
174-
PropertyName("Bind"),
175-
IsDirectiveAttribute);
158+
attribute.SetMetadata(
159+
PropertyName("Bind"),
160+
IsDirectiveAttribute);
176161

177-
attribute.TypeName = "System.Collections.Generic.Dictionary<string, object>";
162+
attribute.TypeName = "System.Collections.Generic.Dictionary<string, object>";
178163

179-
attribute.BindAttributeParameter(parameter =>
180-
{
181-
parameter.Name = "format";
182-
parameter.TypeName = typeof(string).FullName;
183-
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Fallback_Format);
164+
attribute.BindAttributeParameter(parameter =>
165+
{
166+
parameter.Name = "format";
167+
parameter.TypeName = typeof(string).FullName;
168+
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Fallback_Format);
184169

185-
parameter.SetMetadata(Parameters.Format);
186-
});
170+
parameter.SetMetadata(Parameters.Format);
171+
});
187172

188-
attribute.BindAttributeParameter(parameter =>
189-
{
190-
parameter.Name = "event";
191-
parameter.TypeName = typeof(string).FullName;
192-
parameter.SetDocumentation(
193-
DocumentationDescriptor.From(
194-
DocumentationId.BindTagHelper_Fallback_Event, attributeName));
173+
attribute.BindAttributeParameter(parameter =>
174+
{
175+
parameter.Name = "event";
176+
parameter.TypeName = typeof(string).FullName;
177+
parameter.SetDocumentation(
178+
DocumentationDescriptor.From(
179+
DocumentationId.BindTagHelper_Fallback_Event, attributeName));
195180

196-
parameter.SetMetadata(Parameters.Event);
197-
});
181+
parameter.SetMetadata(Parameters.Event);
182+
});
198183

199-
attribute.BindAttributeParameter(parameter =>
200-
{
201-
parameter.Name = "culture";
202-
parameter.TypeName = typeof(CultureInfo).FullName;
203-
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Element_Culture);
184+
attribute.BindAttributeParameter(parameter =>
185+
{
186+
parameter.Name = "culture";
187+
parameter.TypeName = typeof(CultureInfo).FullName;
188+
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Element_Culture);
204189

205-
parameter.SetMetadata(Parameters.Culture);
206-
});
190+
parameter.SetMetadata(Parameters.Culture);
191+
});
207192

208-
attribute.BindAttributeParameter(parameter =>
209-
{
210-
parameter.Name = "get";
211-
parameter.TypeName = typeof(object).FullName;
212-
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Element_Get);
193+
attribute.BindAttributeParameter(parameter =>
194+
{
195+
parameter.Name = "get";
196+
parameter.TypeName = typeof(object).FullName;
197+
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Element_Get);
213198

214-
parameter.SetMetadata(Parameters.Get);
215-
});
199+
parameter.SetMetadata(Parameters.Get);
200+
});
216201

217-
attribute.BindAttributeParameter(parameter =>
218-
{
219-
parameter.Name = "set";
220-
parameter.TypeName = typeof(Delegate).FullName;
221-
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Element_Set);
202+
attribute.BindAttributeParameter(parameter =>
203+
{
204+
parameter.Name = "set";
205+
parameter.TypeName = typeof(Delegate).FullName;
206+
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Element_Set);
222207

223-
parameter.SetMetadata(Parameters.Set);
224-
});
208+
parameter.SetMetadata(Parameters.Set);
209+
});
225210

226-
attribute.BindAttributeParameter(parameter =>
227-
{
228-
parameter.Name = "after";
229-
parameter.TypeName = typeof(Delegate).FullName;
230-
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Element_After);
211+
attribute.BindAttributeParameter(parameter =>
212+
{
213+
parameter.Name = "after";
214+
parameter.TypeName = typeof(Delegate).FullName;
215+
parameter.SetDocumentation(DocumentationDescriptor.BindTagHelper_Element_After);
231216

232-
parameter.SetMetadata(Parameters.After);
233-
});
217+
parameter.SetMetadata(Parameters.After);
234218
});
219+
});
235220

236-
return builder.Build();
237-
}
221+
return builder.Build();
238222
}
239223

240224
private class Collector(

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/CompilationTagHelperFeature.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ public sealed class CompilationTagHelperFeature : RazorEngineFeatureBase, ITagHe
1717

1818
public IReadOnlyList<TagHelperDescriptor> GetDescriptors()
1919
{
20-
var results = new List<TagHelperDescriptor>();
21-
22-
var context = TagHelperDescriptorProviderContext.Create(results);
2320
var compilation = CSharpCompilation.Create("__TagHelpers", references: _referenceFeature.References);
24-
if (IsValidCompilation(compilation))
21+
if (!IsValidCompilation(compilation))
2522
{
26-
context.SetCompilation(compilation);
23+
return [];
2724
}
2825

26+
var results = new List<TagHelperDescriptor>();
27+
var context = new TagHelperDescriptorProviderContext(compilation, results);
28+
2929
for (var i = 0; i < _providers.Length; i++)
3030
{
3131
_providers[i].Execute(context);

0 commit comments

Comments
 (0)