From f7c2e5bffce9dacb49657d741fc8ba44bff75a48 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Tue, 28 Feb 2017 15:55:44 -0800 Subject: [PATCH] [Fixes #5844] Using ExtensionIRNode for Inject directive --- .../IInjectDirectiveTargetExtension.cs | 12 +++ .../InjectDirective.cs | 7 +- .../InjectDirectiveIRNode.cs | 55 ++++++++++++++ .../InjectDirectiveTargetExtension.cs | 44 +++++++++++ .../MvcRazorMvcCoreBuilderExtensions.cs | 2 + .../InjectDirectiveTargetExtensionTest.cs | 75 +++++++++++++++++++ .../InjectDirectiveTest.cs | 41 ++++------ 7 files changed, 207 insertions(+), 29 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Mvc.Razor.Host/IInjectDirectiveTargetExtension.cs create mode 100644 src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirectiveIRNode.cs create mode 100644 src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirectiveTargetExtension.cs create mode 100644 test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/InjectDirectiveTargetExtensionTest.cs diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Host/IInjectDirectiveTargetExtension.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Host/IInjectDirectiveTargetExtension.cs new file mode 100644 index 0000000000..3feb1c17f0 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Host/IInjectDirectiveTargetExtension.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration; + +namespace Microsoft.AspNetCore.Mvc.Razor.Host +{ + public interface IInjectDirectiveTargetExtension : IRuntimeTargetExtension + { + void WriteInjectProperty(CSharpRenderingContext context, InjectDirectiveIRNode node); + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirective.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirective.cs index ec0f4650b5..b624b29503 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirective.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirective.cs @@ -49,14 +49,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host typeName = typeName.Replace("", "<" + modelType + ">"); - var member = new CSharpStatementIRNode() + var injectNode = new InjectDirectiveIRNode() { + TypeName = typeName, + MemberName = memberName, Source = directive.Source, - Content = $"[Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]{Environment.NewLine}public {typeName} {memberName} {{ get; private set; }}", Parent = visitor.Class, }; - visitor.Class.Children.Add(member); + visitor.Class.Children.Add(injectNode); } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirectiveIRNode.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirectiveIRNode.cs new file mode 100644 index 0000000000..4311d0758e --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirectiveIRNode.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Razor.Evolution; +using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration; +using Microsoft.AspNetCore.Razor.Evolution.Intermediate; + +namespace Microsoft.AspNetCore.Mvc.Razor.Host +{ + public class InjectDirectiveIRNode : ExtensionIRNode + { + public string TypeName { get; set; } + + public string MemberName { get; set; } + + public override IList Children { get; } = new RazorIRNode[0]; + + public override RazorIRNode Parent { get; set; } + + public override SourceSpan? Source { get; set; } + + public override void Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + AcceptExtensionNode(this, visitor); + } + + public override TResult Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + return AcceptExtensionNode(this, visitor); + } + + public override void WriteNode(RuntimeTarget target, CSharpRenderingContext context) + { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + + var extension = target.GetExtension(); + extension.WriteInjectProperty(context, this); + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirectiveTargetExtension.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirectiveTargetExtension.cs new file mode 100644 index 0000000000..510dd100b7 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Host/InjectDirectiveTargetExtension.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration; + +namespace Microsoft.AspNetCore.Mvc.Razor.Host +{ + public class InjectDirectiveTargetExtension : IInjectDirectiveTargetExtension + { + private const string RazorInjectAttribute = "[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]"; + + public void WriteInjectProperty(CSharpRenderingContext context, InjectDirectiveIRNode node) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (node == null) + { + throw new ArgumentNullException(nameof(node)); + } + + var property = $"public {node.TypeName} {node.MemberName} {{ get; private set; }}"; + + if (node.Source.HasValue) + { + using (context.Writer.BuildLinePragma(node.Source.Value)) + { + context.Writer + .WriteLine(RazorInjectAttribute) + .WriteLine(property); + } + } + else + { + context.Writer + .WriteLine(RazorInjectAttribute) + .WriteLine(property); + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs index 31161db5d8..19e897f5e9 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs @@ -184,6 +184,8 @@ namespace Microsoft.Extensions.DependencyInjection InjectDirective.Register(b); ModelDirective.Register(b); PageDirective.Register(b); + + b.AddTargetExtension(new InjectDirectiveTargetExtension()); b.Features.Add(new ModelExpressionPass()); b.Features.Add(new PagesPropertyInjectionPass()); diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/InjectDirectiveTargetExtensionTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/InjectDirectiveTargetExtensionTest.cs new file mode 100644 index 0000000000..e499363ef9 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/InjectDirectiveTargetExtensionTest.cs @@ -0,0 +1,75 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Razor.Evolution; +using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration; +using Microsoft.AspNetCore.Razor.Evolution.Legacy; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.Razor.Host +{ + public class InjectDirectiveTargetExtensionTest + { + [Fact] + public void InjectDirectiveTargetExtension_WritesProperty() + { + // Arrange + var context = GetRenderingContext(); + var target = new InjectDirectiveTargetExtension(); + var node = new InjectDirectiveIRNode() + { + TypeName = "PropertyType", + MemberName = "PropertyName", + }; + + // Act + target.WriteInjectProperty(context, node); + + // Assert + Assert.Equal( + "[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]" + Environment.NewLine + + "public PropertyType PropertyName { get; private set; }" + Environment.NewLine, + context.Writer.Builder.ToString()); + } + + [Fact] + public void InjectDirectiveTargetExtension_WritesPropertyWithLinePragma_WhenSourceIsSet() + { + // Arrange + var context = GetRenderingContext(); + var target = new InjectDirectiveTargetExtension(); + var node = new InjectDirectiveIRNode() + { + TypeName = "PropertyType", + MemberName = "PropertyName", + Source = new SourceSpan( + filePath: "test-path", + absoluteIndex: 0, + lineIndex: 1, + characterIndex: 1, + length: 10) + }; + + // Act + target.WriteInjectProperty(context, node); + + // Assert + Assert.Equal( + "#line 2 \"test-path\"" + Environment.NewLine + + "[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]" + Environment.NewLine + + "public PropertyType PropertyName { get; private set; }" + Environment.NewLine + Environment.NewLine + + "#line default" + Environment.NewLine + + "#line hidden" + Environment.NewLine, + context.Writer.Builder.ToString()); + } + + private CSharpRenderingContext GetRenderingContext() + { + return new CSharpRenderingContext() + { + Writer = new CSharpCodeWriter() + }; + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/InjectDirectiveTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/InjectDirectiveTest.cs index 9b62bba4b2..67a1218830 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/InjectDirectiveTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/InjectDirectiveTest.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.IO; using System.Text; using Microsoft.AspNetCore.Razor.Evolution; @@ -36,11 +35,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host Assert.NotNull(@class); Assert.Equal(2, @class.Children.Count); - var statement = Assert.IsType(@class.Children[1]); - Assert.Equal( - "[Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]" + Environment.NewLine + - "public PropertyType PropertyName { get; private set; }", - statement.Content); + var node = Assert.IsType(@class.Children[1]); + Assert.Equal("PropertyType", node.TypeName); + Assert.Equal("PropertyName", node.MemberName); } [Fact] @@ -68,11 +65,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host Assert.NotNull(@class); Assert.Equal(2, @class.Children.Count); - var statement = Assert.IsType(@class.Children[1]); - Assert.Equal( - "[Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]" + Environment.NewLine + - "public PropertyType2 PropertyName { get; private set; }", - statement.Content); + var node = Assert.IsType(@class.Children[1]); + Assert.Equal("PropertyType2", node.TypeName); + Assert.Equal("PropertyName", node.MemberName); } [Fact] @@ -99,11 +94,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host Assert.NotNull(@class); Assert.Equal(2, @class.Children.Count); - var statement = Assert.IsType(@class.Children[1]); - Assert.Equal( - "[Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]" + Environment.NewLine + - "public PropertyType PropertyName { get; private set; }", - statement.Content); + var node = Assert.IsType(@class.Children[1]); + Assert.Equal("PropertyType", node.TypeName); + Assert.Equal("PropertyName", node.MemberName); } [Fact] @@ -131,11 +124,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host Assert.NotNull(@class); Assert.Equal(2, @class.Children.Count); - var statement = Assert.IsType(@class.Children[1]); - Assert.Equal( - "[Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]" + Environment.NewLine + - "public PropertyType PropertyName { get; private set; }", - statement.Content); + var node = Assert.IsType(@class.Children[1]); + Assert.Equal("PropertyType", node.TypeName); + Assert.Equal("PropertyName", node.MemberName); } [Fact] @@ -163,11 +154,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Host Assert.NotNull(@class); Assert.Equal(2, @class.Children.Count); - var statement = Assert.IsType(@class.Children[1]); - Assert.Equal( - "[Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]" + Environment.NewLine + - "public PropertyType PropertyName { get; private set; }", - statement.Content); + var node = Assert.IsType(@class.Children[1]); + Assert.Equal("PropertyType", node.TypeName); + Assert.Equal("PropertyName", node.MemberName); } private RazorCodeDocument CreateDocument(string content)