From e4c4dac3fe6166300096363730bb1d81f0707825 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Fri, 17 Oct 2025 09:58:31 -0700 Subject: [PATCH 1/5] Use shared instances of NodeWriter classes Several of the NodeWriter classes don't have state and can have a shared instance used among callers. These allocations aren't huge in the profiles, but the RazorEditingTests.ScrollingAndTypingInCohosting speedometer test shows about 3 MB of allocating these in the CodeAnalysis process. --- .../DesignTimeNodeWriterTest.cs | 38 +++++----- .../LiteralRuntimeNodeWriterTest.cs | 4 +- .../CodeGeneration/RuntimeNodeWriterTest.cs | 71 ++++++++----------- ...elperHtmlAttributeRuntimeNodeWriterTest.cs | 6 +- .../Extensions/TemplateTargetExtensionTest.cs | 12 ++-- .../CodeGeneration/DefaultCodeTarget.cs | 4 +- .../CodeGeneration/DesignTimeNodeWriter.cs | 4 +- .../CodeGeneration/IntermediateNodeWriter.cs | 2 - .../LiteralRuntimeNodeWriter.cs | 6 +- .../CodeGeneration/RuntimeNodeWriter.cs | 19 ++--- ...TagHelperHtmlAttributeRuntimeNodeWriter.cs | 6 +- .../DefaultTagHelperTargetExtension.cs | 8 +-- .../TestCodeRenderingContext.cs | 4 +- 13 files changed, 87 insertions(+), 97 deletions(-) diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DesignTimeNodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DesignTimeNodeWriterTest.cs index f7e23f0b44e..43d9bced96f 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DesignTimeNodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DesignTimeNodeWriterTest.cs @@ -21,7 +21,7 @@ protected override void ConfigureCodeDocumentProcessor(RazorCodeDocumentProcesso public void WriteUsingDirective_NoSource_WritesContent() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new UsingDirectiveIntermediateNode() @@ -45,7 +45,7 @@ public void WriteUsingDirective_NoSource_WritesContent() public void WriteUsingDirective_WithSource_WritesContentWithLinePragmaAndMapping() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var originalSpan = new SourceSpan("test.cshtml", 0, 0, 0, 6); @@ -81,7 +81,7 @@ public void WriteUsingDirective_WithSource_WritesContentWithLinePragmaAndMapping public void WriteUsingDirective_WithSourceAndLineDirectives_WritesContentWithLinePragmaAndMapping() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var originalSpan = new SourceSpan("test.cshtml", 0, 0, 0, 6); @@ -120,7 +120,7 @@ public void WriteUsingDirective_WithSourceAndLineDirectives_WritesContentWithLin public void WriteCSharpExpression_SkipsLinePragma_WithoutSource() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new CSharpExpressionIntermediateNode(); @@ -143,7 +143,7 @@ public void WriteCSharpExpression_SkipsLinePragma_WithoutSource() public void WriteCSharpExpression_WritesLinePragma_WithSource() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new CSharpExpressionIntermediateNode() @@ -178,7 +178,7 @@ public void WriteCSharpExpression_WritesLinePragma_WithSource() public void WriteCSharpExpression_WithExtensionNode_WritesPadding() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new CSharpExpressionIntermediateNode(); @@ -207,7 +207,7 @@ public void WriteCSharpExpression_WithExtensionNode_WritesPadding() public void WriteCSharpExpression_WithSource_WritesPadding() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new CSharpExpressionIntermediateNode() @@ -246,7 +246,7 @@ public void WriteCSharpExpression_WithSource_WritesPadding() public void WriteCSharpCode_WhitespaceContentWithSource_WritesContent() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new CSharpCodeIntermediateNode() @@ -280,7 +280,7 @@ public void WriteCSharpCode_WhitespaceContentWithSource_WritesContent() public void WriteCSharpCode_SkipsLinePragma_WithoutSource() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new CSharpCodeIntermediateNode(); @@ -303,7 +303,7 @@ public void WriteCSharpCode_SkipsLinePragma_WithoutSource() public void WriteCSharpCode_WritesLinePragma_WithSource() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new CSharpCodeIntermediateNode() @@ -337,7 +337,7 @@ public void WriteCSharpCode_WritesLinePragma_WithSource() public void WriteCSharpCode_WritesPadding_WithSource() { // Arrange - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); var node = new CSharpCodeIntermediateNode() @@ -370,14 +370,14 @@ public void WriteCSharpCode_WritesPadding_WithSource() [Fact] public void WriteCSharpExpressionAttributeValue_RendersCorrectly() { - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); var codeDocument = ProjectEngine.CreateCodeDocument(source); var processor = CreateCodeDocumentProcessor(codeDocument); var documentNode = processor.GetDocumentNode(); - var node = documentNode.Children.OfType().Single().Children[1] as CSharpExpressionAttributeValueIntermediateNode; + var node = (CSharpExpressionAttributeValueIntermediateNode) documentNode.Children.OfType().Single().Children[1]; using var context = TestCodeRenderingContext.CreateDesignTime(source: source); @@ -403,13 +403,13 @@ public void WriteCSharpExpressionAttributeValue_RendersCorrectly() [Fact] public void WriteCSharpCodeAttributeValue_RendersCorrectly() { - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; var content = ""; var sourceDocument = TestRazorSourceDocument.Create(content); var codeDocument = ProjectEngine.CreateCodeDocument(sourceDocument); var processor = CreateCodeDocumentProcessor(codeDocument); var documentNode = processor.GetDocumentNode(); - var node = documentNode.Children.OfType().Single().Children[1] as CSharpCodeAttributeValueIntermediateNode; + var node = (CSharpCodeAttributeValueIntermediateNode) documentNode.Children.OfType().Single().Children[1]; using var context = TestCodeRenderingContext.CreateDesignTime(source: sourceDocument); @@ -435,13 +435,13 @@ public void WriteCSharpCodeAttributeValue_RendersCorrectly() [Fact] public void WriteCSharpCodeAttributeValue_WithExpression_RendersCorrectly() { - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); var codeDocument = ProjectEngine.CreateCodeDocument(source); var processor = CreateCodeDocumentProcessor(codeDocument); var documentNode = processor.GetDocumentNode(); - var node = documentNode.Children.OfType().Single().Children[1] as CSharpCodeAttributeValueIntermediateNode; + var node = (CSharpCodeAttributeValueIntermediateNode)documentNode.Children.OfType().Single().Children[1]; using var context = TestCodeRenderingContext.CreateDesignTime(source: source); @@ -484,7 +484,7 @@ Render Children [InlineData(@"\\SERVER/pages\test.cshtml", @"\\SERVER\pages\test.cshtml")] public void LinePragma_Is_Adjusted_On_Windows(string fileName, string expectedFileName) { - var writer = new DesignTimeNodeWriter(); + var writer = DesignTimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(); Assert.True(context.Options.RemapLinePragmaPathsOnWindows); @@ -528,7 +528,7 @@ public void LinePragma_Is_Adjusted_On_Windows(string fileName, string expectedFi [InlineData(@"\\SERVER/pages\test.cshtml", @"\\SERVER\pages\test.cshtml")] public void LinePragma_Enhanced_Is_Adjusted_On_Windows(string fileName, string expectedFileName) { - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateDesignTime(source: RazorSourceDocument.Create("", fileName)); Assert.True(context.Options.RemapLinePragmaPathsOnWindows); diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/LiteralRuntimeNodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/LiteralRuntimeNodeWriterTest.cs index cbe91b970e1..db5f8d504af 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/LiteralRuntimeNodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/LiteralRuntimeNodeWriterTest.cs @@ -12,7 +12,7 @@ public class LiteralRuntimeNodeWriterTest public void WriteCSharpExpression_UsesWriteLiteral_WritesLinePragma_WithSource() { // Arrange - var writer = new LiteralRuntimeNodeWriter(); + var writer = LiteralRuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new CSharpExpressionIntermediateNode(); @@ -43,7 +43,7 @@ public void WriteCSharpExpression_UsesWriteLiteral_WritesLinePragma_WithSource() public void WriteCSharpExpression_WithMultipleChildren() { // Arrange - var writer = new LiteralRuntimeNodeWriter(); + var writer = LiteralRuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new CSharpExpressionIntermediateNode(); diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs index c09ef8d3c8c..d4b10a0ba16 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs @@ -22,7 +22,7 @@ protected override void ConfigureCodeDocumentProcessor(RazorCodeDocumentProcesso public void WriteUsingDirective_NoSource_WritesContent() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new UsingDirectiveIntermediateNode() @@ -46,7 +46,7 @@ public void WriteUsingDirective_NoSource_WritesContent() public void WriteUsingDirective_WithSource_WritesContentWithLinePragma() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new UsingDirectiveIntermediateNode() @@ -77,7 +77,7 @@ public void WriteUsingDirective_WithSource_WritesContentWithLinePragma() public void WriteUsingDirective_WithSourceAndLineDirectives_WritesContentWithLinePragmaAndMapping() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new UsingDirectiveIntermediateNode() @@ -111,10 +111,7 @@ public void WriteUsingDirective_WithSourceAndLineDirectives_WritesContentWithLin public void WriteCSharpExpression_SkipsLinePragma_WithoutSource() { // Arrange - var writer = new RuntimeNodeWriter() - { - WriteCSharpExpressionMethod = "Test" - }; + var writer = new TestRuntimeNodeWriter(); using var context = TestCodeRenderingContext.CreateRuntime(); @@ -139,10 +136,7 @@ public void WriteCSharpExpression_SkipsLinePragma_WithoutSource() public void WriteCSharpExpression_WritesLinePragma_WithSource() { // Arrange - var writer = new RuntimeNodeWriter() - { - WriteCSharpExpressionMethod = "Test" - }; + var writer = new TestRuntimeNodeWriter(); using var context = TestCodeRenderingContext.CreateRuntime(); @@ -174,10 +168,7 @@ public void WriteCSharpExpression_WritesLinePragma_WithSource() public void WriteCSharpExpression_WithExtensionNode_WritesPadding() { // Arrange - var writer = new RuntimeNodeWriter() - { - WriteCSharpExpressionMethod = "Test" - }; + var writer = new TestRuntimeNodeWriter(); using var context = TestCodeRenderingContext.CreateRuntime(); @@ -207,10 +198,7 @@ iRender Children public void WriteCSharpExpression_WithSource_WritesPadding() { // Arrange - var writer = new RuntimeNodeWriter() - { - WriteCSharpExpressionMethod = "Test" - }; + var writer = new TestRuntimeNodeWriter(); using var context = TestCodeRenderingContext.CreateRuntime(); @@ -254,7 +242,7 @@ Render Children public void WriteCSharpCode_WhitespaceContent_DoesNothing() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new CSharpCodeIntermediateNode(); @@ -273,7 +261,7 @@ public void WriteCSharpCode_WhitespaceContent_DoesNothing() public void WriteCSharpCode_SkipsLinePragma_WithoutSource() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new CSharpCodeIntermediateNode(); @@ -296,7 +284,7 @@ public void WriteCSharpCode_SkipsLinePragma_WithoutSource() public void WriteCSharpCode_WritesLinePragma_WithSource() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new CSharpCodeIntermediateNode(); @@ -327,7 +315,7 @@ public void WriteCSharpCode_WritesLinePragma_WithSource() public void WriteCSharpCode_WritesPadding_WithSource() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new CSharpCodeIntermediateNode(); @@ -358,7 +346,7 @@ public void WriteCSharpCode_WritesPadding_WithSource() public void WriteHtmlLiteral_WithinMaxSize_WritesSingleLiteral() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); // Act @@ -377,7 +365,7 @@ public void WriteHtmlLiteral_WithinMaxSize_WritesSingleLiteral() public void WriteHtmlLiteral_GreaterThanMaxSize_WritesMultipleLiterals() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); // Act @@ -397,7 +385,7 @@ public void WriteHtmlLiteral_GreaterThanMaxSize_WritesMultipleLiterals() public void WriteHtmlLiteral_GreaterThanMaxSize_SingleEmojisSplit() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); // Act @@ -417,7 +405,7 @@ public void WriteHtmlLiteral_GreaterThanMaxSize_SingleEmojisSplit() public void WriteHtmlLiteral_GreaterThanMaxSize_SequencedZeroWithJoinedEmojisSplit() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); // Act @@ -439,7 +427,7 @@ public void WriteHtmlLiteral_GreaterThanMaxSize_SequencedZeroWithJoinedEmojisSpl public void WriteHtmlContent_RendersContentCorrectly() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new HtmlContentIntermediateNode(); @@ -461,7 +449,7 @@ public void WriteHtmlContent_RendersContentCorrectly() public void WriteHtmlContent_LargeStringLiteral_UsesMultipleWrites() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); var node = new HtmlContentIntermediateNode(); @@ -485,7 +473,7 @@ public void WriteHtmlContent_LargeStringLiteral_UsesMultipleWrites() public void WriteHtmlAttribute_RendersCorrectly() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); var codeDocument = ProjectEngine.CreateCodeDocument(source); @@ -514,7 +502,7 @@ Render Children public void WriteHtmlAttributeValue_RendersCorrectly() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); var codeDocument = ProjectEngine.CreateCodeDocument(source); @@ -540,7 +528,7 @@ public void WriteHtmlAttributeValue_RendersCorrectly() public void WriteCSharpExpressionAttributeValue_RendersCorrectly() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); var codeDocument = ProjectEngine.CreateCodeDocument(source); @@ -574,7 +562,7 @@ public void WriteCSharpExpressionAttributeValue_RendersCorrectly() public void WriteCSharpCodeAttributeValue_BuffersResult() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); @@ -612,10 +600,7 @@ public void WriteCSharpCodeAttributeValue_BuffersResult() public void BeginWriterScope_UsesSpecifiedWriter_RendersCorrectly() { // Arrange - var writer = new RuntimeNodeWriter() - { - PushWriterMethod = "TestPushWriter" - }; + var writer = new TestRuntimeNodeWriter(); using var context = TestCodeRenderingContext.CreateRuntime(); @@ -635,10 +620,7 @@ public void BeginWriterScope_UsesSpecifiedWriter_RendersCorrectly() public void EndWriterScope_RendersCorrectly() { // Arrange - var writer = new RuntimeNodeWriter() - { - PopWriterMethod = "TestPopWriter" - }; + var writer = new TestRuntimeNodeWriter(); using var context = TestCodeRenderingContext.CreateRuntime(); @@ -668,4 +650,11 @@ public override void WriteNode(CodeTarget target, CodeRenderingContext context) throw new NotImplementedException(); } } + + public class TestRuntimeNodeWriter : RuntimeNodeWriter + { + public override string WriteCSharpExpressionMethod { get; } = "Test"; + public override string PushWriterMethod { get; } = "TestPushWriter"; + public override string PopWriterMethod { get; } = "TestPopWriter"; + } } diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriterTest.cs index 2bc29757852..6c9cedd6501 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriterTest.cs @@ -19,7 +19,7 @@ protected override void ConfigureCodeDocumentProcessor(RazorCodeDocumentProcesso [Fact] public void WriteHtmlAttributeValue_RendersCorrectly() { - var writer = new TagHelperHtmlAttributeRuntimeNodeWriter(); + var writer = TagHelperHtmlAttributeRuntimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); @@ -45,7 +45,7 @@ public void WriteHtmlAttributeValue_RendersCorrectly() [Fact] public void WriteCSharpExpressionAttributeValue_RendersCorrectly() { - var writer = new TagHelperHtmlAttributeRuntimeNodeWriter(); + var writer = TagHelperHtmlAttributeRuntimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); var codeDocument = ProjectEngine.CreateCodeDocument(source); @@ -78,7 +78,7 @@ public void WriteCSharpExpressionAttributeValue_RendersCorrectly() [Fact] public void WriteCSharpCodeAttributeValue_BuffersResult() { - var writer = new TagHelperHtmlAttributeRuntimeNodeWriter(); + var writer = TagHelperHtmlAttributeRuntimeNodeWriter.Instance; var content = ""; var source = TestRazorSourceDocument.Create(content); diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs index e7783f3fddd..c8c71838d7e 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs @@ -27,11 +27,7 @@ public void WriteTemplate_WritesTemplateCode() TemplateTypeName = "global::TestTemplate" }; - var nodeWriter = new RuntimeNodeWriter() - { - PushWriterMethod = "TestPushWriter", - PopWriterMethod = "TestPopWriter" - }; + var nodeWriter = new TestRuntimeNodeWriter(); using var context = TestCodeRenderingContext.CreateRuntime(nodeWriter: nodeWriter); @@ -49,4 +45,10 @@ Render Children var output = context.CodeWriter.GetText().ToString(); Assert.Equal(expected, output); } + + public class TestRuntimeNodeWriter : RuntimeNodeWriter + { + public override string PushWriterMethod { get; } = "TestPushWriter"; + public override string PopWriterMethod { get; } = "TestPopWriter"; + } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DefaultCodeTarget.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DefaultCodeTarget.cs index c2f7f24cdb7..44e5ed55eb6 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DefaultCodeTarget.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DefaultCodeTarget.cs @@ -12,6 +12,6 @@ internal sealed class DefaultCodeTarget( { public override IntermediateNodeWriter CreateNodeWriter() => Options.DesignTime - ? new DesignTimeNodeWriter() - : new RuntimeNodeWriter(); + ? DesignTimeNodeWriter.Instance + : RuntimeNodeWriter.Instance; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs index 6db086e2d11..2f419e5c9b9 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System; using System.Diagnostics; using Microsoft.AspNetCore.Razor.Language.Extensions; @@ -12,6 +10,8 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration; public class DesignTimeNodeWriter : IntermediateNodeWriter { + public static readonly DesignTimeNodeWriter Instance = new DesignTimeNodeWriter(); + public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node) { if (node.Source is { FilePath: not null } sourceSpan) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/IntermediateNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/IntermediateNodeWriter.cs index 7dcc0bc60e3..a8d5dfdbc87 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/IntermediateNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/IntermediateNodeWriter.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System; using Microsoft.AspNetCore.Razor.Language.Intermediate; diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs index 3be1fef2372..30bae12f2d0 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration; internal class LiteralRuntimeNodeWriter : RuntimeNodeWriter { - public override string WriteCSharpExpressionMethod { get; set; } = "WriteLiteral"; + public static new readonly LiteralRuntimeNodeWriter Instance = new LiteralRuntimeNodeWriter(); + + public override string WriteCSharpExpressionMethod { get; } = "WriteLiteral"; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs index 80fb96663c9..c6bcad746a1 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using System.Globalization; using System.Linq; using Microsoft.AspNetCore.Razor.Language.Intermediate; using Microsoft.AspNetCore.Razor.PooledObjects; @@ -13,21 +12,23 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration; public class RuntimeNodeWriter : IntermediateNodeWriter { - public virtual string WriteCSharpExpressionMethod { get; set; } = "Write"; + public static readonly RuntimeNodeWriter Instance = new RuntimeNodeWriter(); - public virtual string WriteHtmlContentMethod { get; set; } = "WriteLiteral"; + public virtual string WriteCSharpExpressionMethod { get; } = "Write"; - public virtual string BeginWriteAttributeMethod { get; set; } = "BeginWriteAttribute"; + public virtual string WriteHtmlContentMethod { get; } = "WriteLiteral"; - public virtual string EndWriteAttributeMethod { get; set; } = "EndWriteAttribute"; + public virtual string BeginWriteAttributeMethod { get; } = "BeginWriteAttribute"; - public virtual string WriteAttributeValueMethod { get; set; } = "WriteAttributeValue"; + public virtual string EndWriteAttributeMethod { get; } = "EndWriteAttribute"; - public virtual string PushWriterMethod { get; set; } = "PushWriter"; + public virtual string WriteAttributeValueMethod { get; } = "WriteAttributeValue"; - public virtual string PopWriterMethod { get; set; } = "PopWriter"; + public virtual string PushWriterMethod { get; } = "PushWriter"; - public string TemplateTypeName { get; set; } = "Microsoft.AspNetCore.Mvc.Razor.HelperResult"; + public virtual string PopWriterMethod { get; } = "PopWriter"; + + public string TemplateTypeName { get; } = "Microsoft.AspNetCore.Mvc.Razor.HelperResult"; public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs index 3ce267685f3..a9f8b432319 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration; internal class TagHelperHtmlAttributeRuntimeNodeWriter : RuntimeNodeWriter { - public override string WriteAttributeValueMethod { get; set; } = "AddHtmlAttributeValue"; + public static new readonly TagHelperHtmlAttributeRuntimeNodeWriter Instance = new TagHelperHtmlAttributeRuntimeNodeWriter(); + + public override string WriteAttributeValueMethod { get; } = "AddHtmlAttributeValue"; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperTargetExtension.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperTargetExtension.cs index e1f0c661fab..d0bc07de67f 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperTargetExtension.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/DefaultTagHelperTargetExtension.cs @@ -113,7 +113,7 @@ public void WriteTagHelperBody(CodeRenderingContext context, DefaultTagHelperBod using (context.CodeWriter.BuildAsyncLambda()) { // We remove and redirect writers so TagHelper authors can retrieve content. - context.RenderChildren(node, new RuntimeNodeWriter()); + context.RenderChildren(node, RuntimeNodeWriter.Instance); } context.CodeWriter.WriteEndMethodInvocation(); @@ -236,7 +236,7 @@ child is CSharpCodeAttributeValueIntermediateNode || .Write(attributeValueStyleParameter) .WriteEndMethodInvocation(); - context.RenderChildren(node, new TagHelperHtmlAttributeRuntimeNodeWriter()); + context.RenderChildren(node, TagHelperHtmlAttributeRuntimeNodeWriter.Instance); context.CodeWriter .WriteMethodInvocation( @@ -255,7 +255,7 @@ child is CSharpCodeAttributeValueIntermediateNode || // We're building a writing scope around the provided chunks which captures everything written from the // page. Therefore, we do not want to write to any other buffer since we're using the pages buffer to // ensure we capture all content that's written, directly or indirectly. - context.RenderChildren(node, new RuntimeNodeWriter()); + context.RenderChildren(node, RuntimeNodeWriter.Instance); context.CodeWriter .WriteStartAssignment(StringValueBufferVariableName) @@ -358,7 +358,7 @@ public void WriteTagHelperProperty(CodeRenderingContext context, DefaultTagHelpe { context.CodeWriter.WriteMethodInvocation(BeginWriteTagHelperAttributeMethodName); - context.RenderChildren(node, new LiteralRuntimeNodeWriter()); + context.RenderChildren(node, LiteralRuntimeNodeWriter.Instance); context.CodeWriter .WriteStartAssignment(StringValueBufferVariableName) diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/CodeGeneration/TestCodeRenderingContext.cs b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/CodeGeneration/TestCodeRenderingContext.cs index 87293cb7e11..870132e673c 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/CodeGeneration/TestCodeRenderingContext.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/CodeGeneration/TestCodeRenderingContext.cs @@ -15,7 +15,7 @@ public static CodeRenderingContext CreateDesignTime( RazorSourceDocument source = null, IntermediateNodeWriter nodeWriter = null) { - nodeWriter ??= new RuntimeNodeWriter(); + nodeWriter ??= RuntimeNodeWriter.Instance; source ??= TestRazorSourceDocument.Create(); var documentNode = new DocumentIntermediateNode(); @@ -33,7 +33,7 @@ public static CodeRenderingContext CreateRuntime( RazorSourceDocument source = null, IntermediateNodeWriter nodeWriter = null) { - nodeWriter ??= new RuntimeNodeWriter(); + nodeWriter ??= RuntimeNodeWriter.Instance; source ??= TestRazorSourceDocument.Create(); var documentNode = new DocumentIntermediateNode(); From 443c81cafffe86076a293aa727196a462e72a57c Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Fri, 17 Oct 2025 18:47:46 -0700 Subject: [PATCH 2/5] Get rid of backing fields --- .../test/CodeGeneration/RuntimeNodeWriterTest.cs | 6 +++--- .../Extensions/TemplateTargetExtensionTest.cs | 4 ++-- .../CodeGeneration/LiteralRuntimeNodeWriter.cs | 2 +- .../Language/CodeGeneration/RuntimeNodeWriter.cs | 16 ++++++++-------- .../TagHelperHtmlAttributeRuntimeNodeWriter.cs | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs index d4b10a0ba16..fa413af9e2e 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs @@ -653,8 +653,8 @@ public override void WriteNode(CodeTarget target, CodeRenderingContext context) public class TestRuntimeNodeWriter : RuntimeNodeWriter { - public override string WriteCSharpExpressionMethod { get; } = "Test"; - public override string PushWriterMethod { get; } = "TestPushWriter"; - public override string PopWriterMethod { get; } = "TestPopWriter"; + public override string WriteCSharpExpressionMethod { get => "Test"; } + public override string PushWriterMethod { get => "TestPushWriter"; } + public override string PopWriterMethod { get => "TestPopWriter"; } } } diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs index c8c71838d7e..4e220629912 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs @@ -48,7 +48,7 @@ Render Children public class TestRuntimeNodeWriter : RuntimeNodeWriter { - public override string PushWriterMethod { get; } = "TestPushWriter"; - public override string PopWriterMethod { get; } = "TestPopWriter"; + public override string PushWriterMethod { get => "TestPushWriter"; } + public override string PopWriterMethod { get => "TestPopWriter"; } } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs index 30bae12f2d0..aab9e982edb 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs @@ -7,5 +7,5 @@ internal class LiteralRuntimeNodeWriter : RuntimeNodeWriter { public static new readonly LiteralRuntimeNodeWriter Instance = new LiteralRuntimeNodeWriter(); - public override string WriteCSharpExpressionMethod { get; } = "WriteLiteral"; + public override string WriteCSharpExpressionMethod { get => "WriteLiteral"; } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs index c6bcad746a1..5d9b2714dd7 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs @@ -14,21 +14,21 @@ public class RuntimeNodeWriter : IntermediateNodeWriter { public static readonly RuntimeNodeWriter Instance = new RuntimeNodeWriter(); - public virtual string WriteCSharpExpressionMethod { get; } = "Write"; + public virtual string WriteCSharpExpressionMethod { get => "Write"; } - public virtual string WriteHtmlContentMethod { get; } = "WriteLiteral"; + public virtual string WriteHtmlContentMethod { get => "WriteLiteral"; } - public virtual string BeginWriteAttributeMethod { get; } = "BeginWriteAttribute"; + public virtual string BeginWriteAttributeMethod { get => "BeginWriteAttribute"; } - public virtual string EndWriteAttributeMethod { get; } = "EndWriteAttribute"; + public virtual string EndWriteAttributeMethod { get => "EndWriteAttribute"; } - public virtual string WriteAttributeValueMethod { get; } = "WriteAttributeValue"; + public virtual string WriteAttributeValueMethod { get => "WriteAttributeValue"; } - public virtual string PushWriterMethod { get; } = "PushWriter"; + public virtual string PushWriterMethod { get => "PushWriter"; } - public virtual string PopWriterMethod { get; } = "PopWriter"; + public virtual string PopWriterMethod { get => "PopWriter"; } - public string TemplateTypeName { get; } = "Microsoft.AspNetCore.Mvc.Razor.HelperResult"; + public string TemplateTypeName { get => "Microsoft.AspNetCore.Mvc.Razor.HelperResult"; } public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs index a9f8b432319..a43517eb638 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs @@ -7,5 +7,5 @@ internal class TagHelperHtmlAttributeRuntimeNodeWriter : RuntimeNodeWriter { public static new readonly TagHelperHtmlAttributeRuntimeNodeWriter Instance = new TagHelperHtmlAttributeRuntimeNodeWriter(); - public override string WriteAttributeValueMethod { get; } = "AddHtmlAttributeValue"; + public override string WriteAttributeValueMethod { get => "AddHtmlAttributeValue"; } } From 522c327cad2bafb5533a8ee6c13310a574de7559 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Sat, 18 Oct 2025 06:33:10 -0700 Subject: [PATCH 3/5] Formatting --- .../test/CodeGeneration/DesignTimeNodeWriterTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DesignTimeNodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DesignTimeNodeWriterTest.cs index 43d9bced96f..5cab7f66e77 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DesignTimeNodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/DesignTimeNodeWriterTest.cs @@ -377,7 +377,7 @@ public void WriteCSharpExpressionAttributeValue_RendersCorrectly() var codeDocument = ProjectEngine.CreateCodeDocument(source); var processor = CreateCodeDocumentProcessor(codeDocument); var documentNode = processor.GetDocumentNode(); - var node = (CSharpExpressionAttributeValueIntermediateNode) documentNode.Children.OfType().Single().Children[1]; + var node = (CSharpExpressionAttributeValueIntermediateNode)documentNode.Children.OfType().Single().Children[1]; using var context = TestCodeRenderingContext.CreateDesignTime(source: source); @@ -409,7 +409,7 @@ public void WriteCSharpCodeAttributeValue_RendersCorrectly() var codeDocument = ProjectEngine.CreateCodeDocument(sourceDocument); var processor = CreateCodeDocumentProcessor(codeDocument); var documentNode = processor.GetDocumentNode(); - var node = (CSharpCodeAttributeValueIntermediateNode) documentNode.Children.OfType().Single().Children[1]; + var node = (CSharpCodeAttributeValueIntermediateNode)documentNode.Children.OfType().Single().Children[1]; using var context = TestCodeRenderingContext.CreateDesignTime(source: sourceDocument); From acd1a2a89dff1e8e76302538f992519c5ada0eb1 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Mon, 20 Oct 2025 13:58:16 -0700 Subject: [PATCH 4/5] 1) Get rid of TestRuntimeNodeWriter 2) Make classes that expose static Instance have private (or protected) constructors 3) Change from "{ get; => blah; }" syntax to "=> blah;" syntax. --- .../CodeGeneration/RuntimeNodeWriterTest.cs | 31 +++++++------------ .../Extensions/TemplateTargetExtensionTest.cs | 12 ++----- .../CodeGeneration/DesignTimeNodeWriter.cs | 4 +++ .../LiteralRuntimeNodeWriter.cs | 6 +++- .../CodeGeneration/RuntimeNodeWriter.cs | 20 +++++++----- ...TagHelperHtmlAttributeRuntimeNodeWriter.cs | 6 +++- 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs index fa413af9e2e..f52654bbb3d 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs @@ -111,7 +111,7 @@ public void WriteUsingDirective_WithSourceAndLineDirectives_WritesContentWithLin public void WriteCSharpExpression_SkipsLinePragma_WithoutSource() { // Arrange - var writer = new TestRuntimeNodeWriter(); + var writer = new RuntimeNodeWriter(); using var context = TestCodeRenderingContext.CreateRuntime(); @@ -125,7 +125,7 @@ public void WriteCSharpExpression_SkipsLinePragma_WithoutSource() // Assert var csharp = context.CodeWriter.GetText().ToString(); Assert.Equal( -@"Test( +@"Write( i++); ", csharp, @@ -136,7 +136,7 @@ public void WriteCSharpExpression_SkipsLinePragma_WithoutSource() public void WriteCSharpExpression_WritesLinePragma_WithSource() { // Arrange - var writer = new TestRuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); @@ -150,7 +150,7 @@ public void WriteCSharpExpression_WritesLinePragma_WithSource() // Assert var csharp = context.CodeWriter.GetText().ToString(); Assert.Equal( -@"Test( +@"Write( #nullable restore #line (1,1)-(1,4) ""test.cshtml"" i++ @@ -168,7 +168,7 @@ public void WriteCSharpExpression_WritesLinePragma_WithSource() public void WriteCSharpExpression_WithExtensionNode_WritesPadding() { // Arrange - var writer = new TestRuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); @@ -186,7 +186,7 @@ public void WriteCSharpExpression_WithExtensionNode_WritesPadding() // Assert var csharp = context.CodeWriter.GetText().ToString(); Assert.Equal( -@"Test( +@"Write( iRender Children ++); ", @@ -198,7 +198,7 @@ iRender Children public void WriteCSharpExpression_WithSource_WritesPadding() { // Arrange - var writer = new TestRuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); @@ -216,7 +216,7 @@ public void WriteCSharpExpression_WithSource_WritesPadding() // Assert var csharp = context.CodeWriter.GetText().ToString(); Assert.Equal( -@"Test( +@"Write( #nullable restore #line (1,1)-(1,2) ""test.cshtml"" i @@ -600,7 +600,7 @@ public void WriteCSharpCodeAttributeValue_BuffersResult() public void BeginWriterScope_UsesSpecifiedWriter_RendersCorrectly() { // Arrange - var writer = new TestRuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); @@ -610,7 +610,7 @@ public void BeginWriterScope_UsesSpecifiedWriter_RendersCorrectly() // Assert var csharp = context.CodeWriter.GetText().ToString(); Assert.Equal( -@"TestPushWriter(MyWriter); +@"PushWriter(MyWriter); ", csharp, ignoreLineEndingDifferences: true); @@ -620,7 +620,7 @@ public void BeginWriterScope_UsesSpecifiedWriter_RendersCorrectly() public void EndWriterScope_RendersCorrectly() { // Arrange - var writer = new TestRuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(); @@ -630,7 +630,7 @@ public void EndWriterScope_RendersCorrectly() // Assert var csharp = context.CodeWriter.GetText().ToString(); Assert.Equal( -@"TestPopWriter(); +@"PopWriter(); ", csharp, ignoreLineEndingDifferences: true); @@ -650,11 +650,4 @@ public override void WriteNode(CodeTarget target, CodeRenderingContext context) throw new NotImplementedException(); } } - - public class TestRuntimeNodeWriter : RuntimeNodeWriter - { - public override string WriteCSharpExpressionMethod { get => "Test"; } - public override string PushWriterMethod { get => "TestPushWriter"; } - public override string PopWriterMethod { get => "TestPopWriter"; } - } } diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs index 4e220629912..6c022489a40 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/TemplateTargetExtensionTest.cs @@ -27,7 +27,7 @@ public void WriteTemplate_WritesTemplateCode() TemplateTypeName = "global::TestTemplate" }; - var nodeWriter = new TestRuntimeNodeWriter(); + var nodeWriter = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime(nodeWriter: nodeWriter); @@ -36,19 +36,13 @@ public void WriteTemplate_WritesTemplateCode() // Assert var expected = @"item => new global::TestTemplate(async(__razor_template_writer) => { - TestPushWriter(__razor_template_writer); + PushWriter(__razor_template_writer); Render Children - TestPopWriter(); + PopWriter(); } )"; var output = context.CodeWriter.GetText().ToString(); Assert.Equal(expected, output); } - - public class TestRuntimeNodeWriter : RuntimeNodeWriter - { - public override string PushWriterMethod { get => "TestPushWriter"; } - public override string PopWriterMethod { get => "TestPopWriter"; } - } } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs index 2f419e5c9b9..3e9868eea69 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/DesignTimeNodeWriter.cs @@ -12,6 +12,10 @@ public class DesignTimeNodeWriter : IntermediateNodeWriter { public static readonly DesignTimeNodeWriter Instance = new DesignTimeNodeWriter(); + private DesignTimeNodeWriter() + { + } + public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node) { if (node.Source is { FilePath: not null } sourceSpan) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs index aab9e982edb..6631ad30f20 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/LiteralRuntimeNodeWriter.cs @@ -7,5 +7,9 @@ internal class LiteralRuntimeNodeWriter : RuntimeNodeWriter { public static new readonly LiteralRuntimeNodeWriter Instance = new LiteralRuntimeNodeWriter(); - public override string WriteCSharpExpressionMethod { get => "WriteLiteral"; } + private LiteralRuntimeNodeWriter() + { + } + + public override string WriteCSharpExpressionMethod => "WriteLiteral"; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs index 5d9b2714dd7..c01b6bab741 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/RuntimeNodeWriter.cs @@ -14,21 +14,25 @@ public class RuntimeNodeWriter : IntermediateNodeWriter { public static readonly RuntimeNodeWriter Instance = new RuntimeNodeWriter(); - public virtual string WriteCSharpExpressionMethod { get => "Write"; } + public virtual string WriteCSharpExpressionMethod => "Write"; - public virtual string WriteHtmlContentMethod { get => "WriteLiteral"; } + public virtual string WriteHtmlContentMethod => "WriteLiteral"; - public virtual string BeginWriteAttributeMethod { get => "BeginWriteAttribute"; } + public virtual string BeginWriteAttributeMethod => "BeginWriteAttribute"; - public virtual string EndWriteAttributeMethod { get => "EndWriteAttribute"; } + public virtual string EndWriteAttributeMethod => "EndWriteAttribute"; - public virtual string WriteAttributeValueMethod { get => "WriteAttributeValue"; } + public virtual string WriteAttributeValueMethod => "WriteAttributeValue"; - public virtual string PushWriterMethod { get => "PushWriter"; } + public virtual string PushWriterMethod => "PushWriter"; - public virtual string PopWriterMethod { get => "PopWriter"; } + public virtual string PopWriterMethod => "PopWriter"; - public string TemplateTypeName { get => "Microsoft.AspNetCore.Mvc.Razor.HelperResult"; } + public const string TemplateTypeName = "Microsoft.AspNetCore.Mvc.Razor.HelperResult"; + + protected RuntimeNodeWriter() + { + } public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node) { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs index a43517eb638..df89c0a39d5 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/TagHelperHtmlAttributeRuntimeNodeWriter.cs @@ -7,5 +7,9 @@ internal class TagHelperHtmlAttributeRuntimeNodeWriter : RuntimeNodeWriter { public static new readonly TagHelperHtmlAttributeRuntimeNodeWriter Instance = new TagHelperHtmlAttributeRuntimeNodeWriter(); - public override string WriteAttributeValueMethod { get => "AddHtmlAttributeValue"; } + public override string WriteAttributeValueMethod => "AddHtmlAttributeValue"; + + private TagHelperHtmlAttributeRuntimeNodeWriter() + { + } } From 11b5e4f7ab1ac9e2b3db7b6e13d9b3446946d11e Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Mon, 20 Oct 2025 16:34:14 -0700 Subject: [PATCH 5/5] Fix build break after making ctors less accessible --- .../test/CodeGeneration/RuntimeNodeWriterTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs index f52654bbb3d..2384f4452b6 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/CodeGeneration/RuntimeNodeWriterTest.cs @@ -111,7 +111,7 @@ public void WriteUsingDirective_WithSourceAndLineDirectives_WritesContentWithLin public void WriteCSharpExpression_SkipsLinePragma_WithoutSource() { // Arrange - var writer = new RuntimeNodeWriter(); + var writer = RuntimeNodeWriter.Instance; using var context = TestCodeRenderingContext.CreateRuntime();