Rewrite of code rendering context
This commit is contained in:
parent
8dfba25d59
commit
7408bcd025
|
|
@ -2,6 +2,7 @@
|
|||
// 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.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
|
|
@ -18,11 +19,15 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
public abstract string DocumentKind { get; }
|
||||
|
||||
public abstract ItemCollection Items { get; }
|
||||
|
||||
public abstract IEnumerable<IntermediateNode> Ancestors { get; }
|
||||
|
||||
public abstract IntermediateNodeWriter NodeWriter { get; }
|
||||
|
||||
public abstract RazorCodeGenerationOptions Options { get; }
|
||||
|
||||
public abstract IntermediateNode Parent { get; }
|
||||
|
||||
public abstract RazorSourceDocument SourceDocument { get; }
|
||||
|
||||
public abstract Scope CreateScope();
|
||||
|
|
@ -56,48 +61,5 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
_context.EndScope();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// All bits below here are temporary
|
||||
|
||||
#region Temporary TagHelper bits
|
||||
|
||||
internal TagHelperRenderingContext TagHelperRenderingContext { get; set; }
|
||||
|
||||
internal TagHelperRenderingContextScope Push(TagHelperRenderingContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var scope = new TagHelperRenderingContextScope(this, TagHelperRenderingContext);
|
||||
TagHelperRenderingContext = context;
|
||||
return scope;
|
||||
}
|
||||
|
||||
internal struct TagHelperRenderingContextScope : IDisposable
|
||||
{
|
||||
private readonly CodeRenderingContext _context;
|
||||
private readonly TagHelperRenderingContext _renderingContext;
|
||||
|
||||
public TagHelperRenderingContextScope(CodeRenderingContext context, TagHelperRenderingContext renderingContext)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
_context = context;
|
||||
_renderingContext = renderingContext;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_context.TagHelperRenderingContext = _renderingContext;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
{
|
||||
internal class DefaultCodeRenderingContext : CodeRenderingContext
|
||||
{
|
||||
private readonly Stack<IntermediateNode> _ancestors;
|
||||
private readonly RazorCodeDocument _codeDocument;
|
||||
private readonly DocumentIntermediateNode _documentNode;
|
||||
|
||||
private readonly List<ScopeInternal> _scopes;
|
||||
|
||||
public DefaultCodeRenderingContext(
|
||||
|
|
@ -52,12 +52,11 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
_documentNode = documentNode;
|
||||
Options = options;
|
||||
|
||||
_ancestors = new Stack<IntermediateNode>();
|
||||
Diagnostics = new RazorDiagnosticCollection();
|
||||
Items = new ItemCollection();
|
||||
LineMappings = new List<LineMapping>();
|
||||
|
||||
TagHelperRenderingContext = new TagHelperRenderingContext();
|
||||
|
||||
var diagnostics = _documentNode.GetAllDiagnostics();
|
||||
for (var i = 0; i < diagnostics.Count; i++)
|
||||
{
|
||||
|
|
@ -81,6 +80,10 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
// This will be initialized by the document writer when the context is 'live'.
|
||||
public IntermediateNodeVisitor Visitor { get; set; }
|
||||
|
||||
public override IEnumerable<IntermediateNode> Ancestors => _ancestors;
|
||||
|
||||
internal Stack<IntermediateNode> AncestorsInternal => _ancestors;
|
||||
|
||||
public override CodeWriter CodeWriter { get; }
|
||||
|
||||
public override RazorDiagnosticCollection Diagnostics { get; }
|
||||
|
|
@ -95,6 +98,8 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
|
||||
public override RazorCodeGenerationOptions Options { get; }
|
||||
|
||||
public override IntermediateNode Parent => _ancestors.Count == 0 ? null : _ancestors.Peek();
|
||||
|
||||
public override RazorSourceDocument SourceDocument => _codeDocument.Source;
|
||||
|
||||
private ScopeInternal Current => _scopes[_scopes.Count - 1];
|
||||
|
|
@ -154,10 +159,14 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
throw new ArgumentNullException(nameof(node));
|
||||
}
|
||||
|
||||
_ancestors.Push(node);
|
||||
|
||||
for (var i = 0; i < node.Children.Count; i++)
|
||||
{
|
||||
Visitor.Visit(node.Children[i]);
|
||||
}
|
||||
|
||||
_ancestors.Pop();
|
||||
}
|
||||
|
||||
public override void RenderNode(IntermediateNode node)
|
||||
|
|
|
|||
|
|
@ -200,12 +200,7 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
|
||||
public override void VisitTagHelper(TagHelperIntermediateNode node)
|
||||
{
|
||||
var tagHelperRenderingContext = new TagHelperRenderingContext();
|
||||
|
||||
using (Context.Push(tagHelperRenderingContext))
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public override void VisitDefault(IntermediateNode node)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
@ -75,6 +76,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
public void WriteTagHelperBody(CodeRenderingContext context, DefaultTagHelperBodyIntermediateNode node)
|
||||
{
|
||||
if (context.Parent as TagHelperIntermediateNode == null)
|
||||
{
|
||||
var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (DesignTime)
|
||||
{
|
||||
context.RenderChildren(node);
|
||||
|
|
@ -121,6 +128,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
public void WriteTagHelperCreate(CodeRenderingContext context, DefaultTagHelperCreateIntermediateNode node)
|
||||
{
|
||||
if (context.Parent as TagHelperIntermediateNode == null)
|
||||
{
|
||||
var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
context.CodeWriter
|
||||
.WriteStartAssignment(node.Field)
|
||||
.Write(CreateTagHelperMethodName)
|
||||
|
|
@ -137,6 +150,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
public void WriteTagHelperExecute(CodeRenderingContext context, DefaultTagHelperExecuteIntermediateNode node)
|
||||
{
|
||||
if (context.Parent as TagHelperIntermediateNode == null)
|
||||
{
|
||||
var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (!DesignTime)
|
||||
{
|
||||
context.CodeWriter
|
||||
|
|
@ -178,6 +197,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
public void WriteTagHelperHtmlAttribute(CodeRenderingContext context, DefaultTagHelperHtmlAttributeIntermediateNode node)
|
||||
{
|
||||
if (context.Parent as TagHelperIntermediateNode == null)
|
||||
{
|
||||
var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (DesignTime)
|
||||
{
|
||||
context.RenderChildren(node);
|
||||
|
|
@ -261,22 +286,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
public void WriteTagHelperProperty(CodeRenderingContext context, DefaultTagHelperPropertyIntermediateNode node)
|
||||
{
|
||||
var tagHelperRenderingContext = context.TagHelperRenderingContext;
|
||||
var propertyName = node.BoundAttribute.GetPropertyName();
|
||||
var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, node.Field, node.AttributeName, node.BoundAttribute);
|
||||
var tagHelperNode = context.Parent as TagHelperIntermediateNode;
|
||||
if (context.Parent == null)
|
||||
{
|
||||
var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
if (!DesignTime)
|
||||
{
|
||||
// Ensure that the property we're trying to set has initialized its dictionary bound properties.
|
||||
if (node.IsIndexerNameMatch &&
|
||||
tagHelperRenderingContext.VerifiedPropertyDictionaries.Add($"{node.TagHelper.GetTypeName()}.{propertyName}"))
|
||||
object.ReferenceEquals(FindFirstUseOfIndexer(tagHelperNode, node), node))
|
||||
{
|
||||
// Throw a reasonable Exception at runtime if the dictionary property is null.
|
||||
context.CodeWriter
|
||||
.Write("if (")
|
||||
.Write(node.Field)
|
||||
.Write(".")
|
||||
.Write(propertyName)
|
||||
.Write(node.Property)
|
||||
.WriteLine(" == null)");
|
||||
using (context.CodeWriter.BuildScope())
|
||||
{
|
||||
|
|
@ -290,34 +318,40 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(node.TagHelper.GetTypeName())
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(propertyName)
|
||||
.WriteStringLiteral(node.Property)
|
||||
.WriteEndMethodInvocation(endLine: false) // End of method call
|
||||
.WriteEndMethodInvocation(); // End of new expression / throw statement
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out var previousValueAccessor))
|
||||
// If this is not the first use of the attribute value, we need to evaluate the expression and assign it to
|
||||
// the tag helper property.
|
||||
//
|
||||
// Otherwise, the value has already been computed and assigned to another tag helper. We just need to
|
||||
// copy from that tag helper to this one.
|
||||
//
|
||||
// This is important because we can't evaluate the expression twice due to side-effects.
|
||||
var firstUseOfAttribute = FindFirstUseOfAttribute(tagHelperNode, node);
|
||||
if (!object.ReferenceEquals(firstUseOfAttribute, node))
|
||||
{
|
||||
// If we get here, this value has already been used. We just need to copy the value.
|
||||
context.CodeWriter
|
||||
.WriteStartAssignment(propertyValueAccessor)
|
||||
.Write(previousValueAccessor)
|
||||
.WriteStartAssignment(GetPropertyAccessor(node))
|
||||
.Write(GetPropertyAccessor(firstUseOfAttribute))
|
||||
.WriteLine(";");
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
|
||||
}
|
||||
|
||||
// If we get there, this is the first time seeing this property so we need to evaluate the expression.
|
||||
if (node.BoundAttribute.IsStringProperty || (node.IsIndexerNameMatch && node.BoundAttribute.IsIndexerStringProperty))
|
||||
{
|
||||
if (DesignTime)
|
||||
{
|
||||
context.RenderChildren(node);
|
||||
|
||||
context.CodeWriter.WriteStartAssignment(propertyValueAccessor);
|
||||
context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node));
|
||||
if (node.Children.Count == 1 && node.Children.First() is HtmlContentIntermediateNode htmlNode)
|
||||
{
|
||||
var content = GetContent(htmlNode);
|
||||
|
|
@ -341,7 +375,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
context.CodeWriter
|
||||
.WriteStartAssignment(StringValueBufferVariableName)
|
||||
.WriteMethodInvocation(EndWriteTagHelperAttributeMethodName)
|
||||
.WriteStartAssignment(propertyValueAccessor)
|
||||
.WriteStartAssignment(GetPropertyAccessor(node))
|
||||
.Write(StringValueBufferVariableName)
|
||||
.WriteLine(";");
|
||||
}
|
||||
|
|
@ -355,7 +389,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
using (context.CodeWriter.BuildLinePragma(node.Source))
|
||||
{
|
||||
var assignmentPrefixLength = propertyValueAccessor.Length + " = ".Length;
|
||||
var accessor = GetPropertyAccessor(node);
|
||||
var assignmentPrefixLength = accessor.Length + " = ".Length;
|
||||
if (node.BoundAttribute.IsEnum &&
|
||||
node.Children.Count == 1 &&
|
||||
node.Children.First() is IntermediateToken token &&
|
||||
|
|
@ -369,7 +404,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
|
||||
context.CodeWriter
|
||||
.WriteStartAssignment(propertyValueAccessor)
|
||||
.WriteStartAssignment(accessor)
|
||||
.Write("global::")
|
||||
.Write(node.BoundAttribute.TypeName)
|
||||
.Write(".");
|
||||
|
|
@ -381,7 +416,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
context.CodeWriter.WritePadding(assignmentPrefixLength, node.Source, context);
|
||||
}
|
||||
|
||||
context.CodeWriter.WriteStartAssignment(propertyValueAccessor);
|
||||
context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node));
|
||||
}
|
||||
|
||||
RenderTagHelperAttributeInline(context, node, node.Source);
|
||||
|
|
@ -393,7 +428,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
{
|
||||
using (context.CodeWriter.BuildLinePragma(node.Source))
|
||||
{
|
||||
context.CodeWriter.WriteStartAssignment(propertyValueAccessor);
|
||||
context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node));
|
||||
|
||||
if (node.BoundAttribute.IsEnum &&
|
||||
node.Children.Count == 1 &&
|
||||
|
|
@ -422,7 +457,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
ExecutionContextAddTagHelperAttributeMethodName)
|
||||
.WriteStringLiteral(node.AttributeName)
|
||||
.WriteParameterSeparator()
|
||||
.Write(propertyValueAccessor)
|
||||
.Write(GetPropertyAccessor(node))
|
||||
.WriteParameterSeparator()
|
||||
.Write($"global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.{node.AttributeStructure}")
|
||||
.WriteEndMethodInvocation();
|
||||
|
|
@ -548,6 +583,45 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
}
|
||||
|
||||
private static DefaultTagHelperPropertyIntermediateNode FindFirstUseOfIndexer(
|
||||
TagHelperIntermediateNode tagHelperNode,
|
||||
DefaultTagHelperPropertyIntermediateNode propertyNode)
|
||||
{
|
||||
Debug.Assert(tagHelperNode.Children.Contains(propertyNode));
|
||||
Debug.Assert(propertyNode.IsIndexerNameMatch);
|
||||
|
||||
for (var i = 0; i < tagHelperNode.Children.Count; i++)
|
||||
{
|
||||
if (tagHelperNode.Children[i] is DefaultTagHelperPropertyIntermediateNode otherPropertyNode &&
|
||||
otherPropertyNode.TagHelper.Equals(propertyNode.TagHelper) &&
|
||||
otherPropertyNode.BoundAttribute.Equals(propertyNode.BoundAttribute) &&
|
||||
otherPropertyNode.IsIndexerNameMatch)
|
||||
{
|
||||
return otherPropertyNode;
|
||||
}
|
||||
}
|
||||
|
||||
// This is unreachable, we should find 'propertyNode' in the list of children.
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private static DefaultTagHelperPropertyIntermediateNode FindFirstUseOfAttribute(
|
||||
TagHelperIntermediateNode tagHelperNode,
|
||||
DefaultTagHelperPropertyIntermediateNode propertyNode)
|
||||
{
|
||||
for (var i = 0; i < tagHelperNode.Children.Count; i++)
|
||||
{
|
||||
if (tagHelperNode.Children[i] is DefaultTagHelperPropertyIntermediateNode otherPropertyNode &&
|
||||
string.Equals(otherPropertyNode.AttributeName, propertyNode.AttributeName, StringComparison.Ordinal))
|
||||
{
|
||||
return otherPropertyNode;
|
||||
}
|
||||
}
|
||||
|
||||
// This is unreachable, we should find 'propertyNode' in the list of children.
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private string GetContent(HtmlContentIntermediateNode node)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
|
@ -562,17 +636,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string GetTagHelperPropertyAccessor(
|
||||
bool isIndexerNameMatch,
|
||||
string tagHelperVariableName,
|
||||
string attributeName,
|
||||
BoundAttributeDescriptor descriptor)
|
||||
private static string GetPropertyAccessor(DefaultTagHelperPropertyIntermediateNode node)
|
||||
{
|
||||
var propertyAccessor = $"{tagHelperVariableName}.{descriptor.GetPropertyName()}";
|
||||
var propertyAccessor = $"{node.Field}.{node.Property}";
|
||||
|
||||
if (isIndexerNameMatch)
|
||||
if (node.IsIndexerNameMatch)
|
||||
{
|
||||
var dictionaryKey = attributeName.Substring(descriptor.IndexerNamePrefix.Length);
|
||||
var dictionaryKey = node.AttributeName.Substring(node.BoundAttribute.IndexerNamePrefix.Length);
|
||||
propertyAccessor += $"[\"{dictionaryKey}\"]";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
|
|
@ -50,6 +52,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
public void WriteTagHelperHtmlAttribute(CodeRenderingContext context, PreallocatedTagHelperHtmlAttributeIntermediateNode node)
|
||||
{
|
||||
if (context.Parent as TagHelperIntermediateNode == null)
|
||||
{
|
||||
var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
context.CodeWriter
|
||||
.WriteStartInstanceMethodInvocation(ExecutionContextVariableName, ExecutionContextAddHtmlAttributeMethodName)
|
||||
.Write(node.VariableName)
|
||||
|
|
@ -75,20 +83,23 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
public void WriteTagHelperProperty(CodeRenderingContext context, PreallocatedTagHelperPropertyIntermediateNode node)
|
||||
{
|
||||
var propertyName = node.BoundAttribute.GetPropertyName();
|
||||
var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, node.Field, node.AttributeName, node.BoundAttribute);
|
||||
var attributeValueAccessor = $"{node.VariableName}.Value" /* ORIGINAL: TagHelperAttributeValuePropertyName */;
|
||||
var tagHelperNode = context.Parent as TagHelperIntermediateNode;
|
||||
if (tagHelperNode == null)
|
||||
{
|
||||
var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode));
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
// Ensure that the property we're trying to set has initialized its dictionary bound properties.
|
||||
if (node.IsIndexerNameMatch &&
|
||||
context.TagHelperRenderingContext.VerifiedPropertyDictionaries.Add($"{node.TagHelper.GetTypeName()}.{propertyName}"))
|
||||
object.ReferenceEquals(FindFirstUseOfIndexer(tagHelperNode, node), node))
|
||||
{
|
||||
// Throw a reasonable Exception at runtime if the dictionary property is null.
|
||||
context.CodeWriter
|
||||
.Write("if (")
|
||||
.Write(node.Field)
|
||||
.Write(".")
|
||||
.Write(propertyName)
|
||||
.Write(node.Property)
|
||||
.WriteLine(" == null)");
|
||||
using (context.CodeWriter.BuildScope())
|
||||
{
|
||||
|
|
@ -102,33 +113,51 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(node.TagHelper.GetTypeName())
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(propertyName)
|
||||
.WriteStringLiteral(node.Property)
|
||||
.WriteEndMethodInvocation(endLine: false) // End of method call
|
||||
.WriteEndMethodInvocation(); // End of new expression / throw statement
|
||||
}
|
||||
}
|
||||
|
||||
context.CodeWriter
|
||||
.WriteStartAssignment(propertyValueAccessor)
|
||||
.WriteStartAssignment(GetPropertyAccessor(node))
|
||||
.Write("(string)")
|
||||
.Write(attributeValueAccessor)
|
||||
.Write($"{node.VariableName}.Value")
|
||||
.WriteLine(";")
|
||||
.WriteStartInstanceMethodInvocation(ExecutionContextVariableName, ExecutionContextAddTagHelperAttributeMethodName)
|
||||
.Write(node.VariableName)
|
||||
.WriteEndMethodInvocation();
|
||||
}
|
||||
|
||||
private static string GetTagHelperPropertyAccessor(
|
||||
bool isIndexerNameMatch,
|
||||
string tagHelperVariableName,
|
||||
string attributeName,
|
||||
BoundAttributeDescriptor descriptor)
|
||||
private static PreallocatedTagHelperPropertyIntermediateNode FindFirstUseOfIndexer(
|
||||
TagHelperIntermediateNode tagHelperNode,
|
||||
PreallocatedTagHelperPropertyIntermediateNode propertyNode)
|
||||
{
|
||||
var propertyAccessor = $"{tagHelperVariableName}.{descriptor.GetPropertyName()}";
|
||||
Debug.Assert(tagHelperNode.Children.Contains(propertyNode));
|
||||
Debug.Assert(propertyNode.IsIndexerNameMatch);
|
||||
|
||||
if (isIndexerNameMatch)
|
||||
for (var i = 0; i < tagHelperNode.Children.Count; i++)
|
||||
{
|
||||
var dictionaryKey = attributeName.Substring(descriptor.IndexerNamePrefix.Length);
|
||||
if (tagHelperNode.Children[i] is PreallocatedTagHelperPropertyIntermediateNode otherPropertyNode &&
|
||||
otherPropertyNode.TagHelper.Equals(propertyNode.TagHelper) &&
|
||||
otherPropertyNode.BoundAttribute.Equals(propertyNode.BoundAttribute) &&
|
||||
otherPropertyNode.IsIndexerNameMatch)
|
||||
{
|
||||
return otherPropertyNode;
|
||||
}
|
||||
}
|
||||
|
||||
// This is unreachable, we should find 'propertyNode' in the list of children.
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private static string GetPropertyAccessor(PreallocatedTagHelperPropertyIntermediateNode node)
|
||||
{
|
||||
var propertyAccessor = $"{node.Field}.{node.Property}";
|
||||
|
||||
if (node.IsIndexerNameMatch)
|
||||
{
|
||||
var dictionaryKey = node.AttributeName.Substring(node.BoundAttribute.IndexerNamePrefix.Length);
|
||||
propertyAccessor += $"[\"{dictionaryKey}\"]";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -584,6 +584,20 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
internal static string FormatRenderingContextRequiresDelegate(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("RenderingContextRequiresDelegate"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' node type can only be used as a direct child of a '{1}' node.
|
||||
/// </summary>
|
||||
internal static string IntermediateNodes_InvalidParentNode
|
||||
{
|
||||
get => GetString("IntermediateNodes_InvalidParentNode");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' node type can only be used as a direct child of a '{1}' node.
|
||||
/// </summary>
|
||||
internal static string FormatIntermediateNodes_InvalidParentNode(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("IntermediateNodes_InvalidParentNode"), p0, p1);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -240,4 +240,7 @@
|
|||
<data name="RenderingContextRequiresDelegate" xml:space="preserve">
|
||||
<value>The '{0}' requires a '{1}' delegate to be set.</value>
|
||||
</data>
|
||||
<data name="IntermediateNodes_InvalidParentNode" xml:space="preserve">
|
||||
<value>The '{0}' node type can only be used as a direct child of a '{1}' node.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -71,6 +71,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperBodyIntermediateNode()
|
||||
{
|
||||
Children =
|
||||
|
|
@ -78,6 +79,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
new CSharpExpressionIntermediateNode(),
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperBody(context, node);
|
||||
|
|
@ -98,6 +101,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperBodyIntermediateNode()
|
||||
{
|
||||
Children =
|
||||
|
|
@ -107,6 +111,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
TagMode = TagMode.SelfClosing,
|
||||
TagName = "p",
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperBody(context, node);
|
||||
|
|
@ -130,11 +136,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperCreateIntermediateNode()
|
||||
{
|
||||
Field = "__TestNamespace_MyTagHelper",
|
||||
Type = "TestNamespace.MyTagHelper",
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperCreate(context, node);
|
||||
|
|
@ -155,11 +164,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperCreateIntermediateNode()
|
||||
{
|
||||
Field = "__TestNamespace_MyTagHelper",
|
||||
Type = "TestNamespace.MyTagHelper",
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperCreate(context, node);
|
||||
|
|
@ -181,7 +193,10 @@ __tagHelperExecutionContext.Add(__TestNamespace_MyTagHelper);
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperExecuteIntermediateNode();
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperExecute(context, node);
|
||||
|
|
@ -201,7 +216,10 @@ __tagHelperExecutionContext.Add(__TestNamespace_MyTagHelper);
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperExecuteIntermediateNode();
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperExecute(context, node);
|
||||
|
|
@ -228,6 +246,7 @@ __tagHelperExecutionContext = __tagHelperScopeManager.End();
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperHtmlAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = "name",
|
||||
|
|
@ -244,6 +263,8 @@ __tagHelperExecutionContext = __tagHelperScopeManager.End();
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperHtmlAttribute(context, node);
|
||||
|
|
@ -265,6 +286,7 @@ Render Children
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperHtmlAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = "name",
|
||||
|
|
@ -277,6 +299,8 @@ Render Children
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperHtmlAttribute(context, node);
|
||||
|
|
@ -300,6 +324,7 @@ __tagHelperExecutionContext.AddHtmlAttribute(""name"", Html.Raw(__tagHelperStrin
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperHtmlAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = "name",
|
||||
|
|
@ -316,6 +341,8 @@ __tagHelperExecutionContext.AddHtmlAttribute(""name"", Html.Raw(__tagHelperStrin
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperHtmlAttribute(context, node);
|
||||
|
|
@ -339,6 +366,7 @@ EndAddHtmlAttributeValues(__tagHelperExecutionContext);
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
|
|
@ -356,6 +384,8 @@ EndAddHtmlAttributeValues(__tagHelperExecutionContext);
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -377,6 +407,7 @@ __InputTagHelper.StringProp = ""value"";
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
|
|
@ -394,6 +425,8 @@ __InputTagHelper.StringProp = ""value"";
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -415,6 +448,7 @@ __InputTagHelper.StringProp = string.Empty;
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
|
|
@ -433,6 +467,8 @@ __InputTagHelper.StringProp = string.Empty;
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -450,6 +486,50 @@ __InputTagHelper.IntProp = 32;
|
|||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
// If a value is bound to multiple tag helpers, we want to make sure to only render the first
|
||||
// occurrence of the expression due to side-effects.
|
||||
[Fact]
|
||||
public void WriteTagHelperProperty_DesignTime_NonStringProperty_SecondUseOfAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node1 = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
// We only look at the attribute name here.
|
||||
AttributeName = "bound",
|
||||
Field = "__OtherTagHelper",
|
||||
Property = "IntProp",
|
||||
};
|
||||
var node2 = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
AttributeStructure = AttributeStructure.DoubleQuotes,
|
||||
BoundAttribute = IntPropertyTagHelper.BoundAttributes.Single(),
|
||||
Field = "__InputTagHelper",
|
||||
IsIndexerNameMatch = false,
|
||||
Property = "IntProp",
|
||||
TagHelper = IntPropertyTagHelper,
|
||||
Source = Span,
|
||||
};
|
||||
tagHelperNode.Children.Add(node1);
|
||||
tagHelperNode.Children.Add(node2);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node2);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.Builder.ToString();
|
||||
Assert.Equal(
|
||||
@"__InputTagHelper.IntProp = __OtherTagHelper.IntProp;
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTagHelperProperty_DesignTime_NonStringProperty_RendersCorrectly_WithoutLocation()
|
||||
{
|
||||
|
|
@ -457,6 +537,7 @@ __InputTagHelper.IntProp = 32;
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
|
|
@ -474,6 +555,8 @@ __InputTagHelper.IntProp = 32;
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -494,6 +577,7 @@ __InputTagHelper.IntProp = 32;
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "foo-bound",
|
||||
|
|
@ -512,6 +596,8 @@ __InputTagHelper.IntProp = 32;
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -536,6 +622,7 @@ __InputTagHelper.IntIndexer[""bound""] = 32;
|
|||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "foo-bound",
|
||||
|
|
@ -553,6 +640,8 @@ __InputTagHelper.IntIndexer[""bound""] = 32;
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -573,6 +662,7 @@ __InputTagHelper.IntIndexer[""bound""] = 32;
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
|
|
@ -590,6 +680,8 @@ __InputTagHelper.IntIndexer[""bound""] = 32;
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -616,6 +708,7 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""bound"", __InputTagHelper.St
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
|
|
@ -634,6 +727,8 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""bound"", __InputTagHelper.St
|
|||
}
|
||||
},
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -652,6 +747,50 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""bound"", __InputTagHelper.In
|
|||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
// If a value is bound to multiple tag helpers, we want to make sure to only render the first
|
||||
// occurrence of the expression due to side-effects.
|
||||
[Fact]
|
||||
public void WriteTagHelperProperty_Runtime_NonStringProperty_SecondUseOfAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node1 = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
// We only look at the attribute name here.
|
||||
AttributeName = "bound",
|
||||
Field = "__OtherTagHelper",
|
||||
Property = "IntProp",
|
||||
};
|
||||
var node2 = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
AttributeStructure = AttributeStructure.DoubleQuotes,
|
||||
BoundAttribute = IntPropertyTagHelper.BoundAttributes.Single(),
|
||||
Field = "__InputTagHelper",
|
||||
IsIndexerNameMatch = false,
|
||||
Property = "IntProp",
|
||||
TagHelper = IntPropertyTagHelper,
|
||||
Source = Span,
|
||||
};
|
||||
tagHelperNode.Children.Add(node1);
|
||||
tagHelperNode.Children.Add(node2);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node2);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.Builder.ToString();
|
||||
Assert.Equal(
|
||||
@"__InputTagHelper.IntProp = __OtherTagHelper.IntProp;
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteTagHelperProperty_Runtime_NonStringProperty_RendersCorrectly_WithoutLocation()
|
||||
{
|
||||
|
|
@ -659,6 +798,7 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""bound"", __InputTagHelper.In
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "bound",
|
||||
|
|
@ -676,6 +816,8 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""bound"", __InputTagHelper.In
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -697,6 +839,7 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""bound"", __InputTagHelper.In
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "foo-bound",
|
||||
|
|
@ -715,6 +858,8 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""bound"", __InputTagHelper.In
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -729,6 +874,71 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""bound"", __InputTagHelper.In
|
|||
#line 3 ""test.cshtml""
|
||||
__InputTagHelper.IntIndexer[""bound""] = 32;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute(""foo-bound"", __InputTagHelper.IntIndexer[""bound""], global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact] // We should only emit the validation code for the first use of an indexer property.
|
||||
public void WriteTagHelperProperty_Runtime_NonStringIndexer_MultipleValues()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node1 = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "foo-first",
|
||||
AttributeStructure = AttributeStructure.DoubleQuotes,
|
||||
BoundAttribute = IntIndexerTagHelper.BoundAttributes.Single(),
|
||||
Field = "__InputTagHelper",
|
||||
IsIndexerNameMatch = true,
|
||||
Property = "IntIndexer",
|
||||
TagHelper = IntIndexerTagHelper,
|
||||
Source = Span,
|
||||
Children =
|
||||
{
|
||||
new CSharpExpressionIntermediateNode()
|
||||
{
|
||||
Children = { new IntermediateToken { Kind = IntermediateToken.TokenKind.CSharp, Content = "17", } },
|
||||
}
|
||||
}
|
||||
};
|
||||
var node2 = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "foo-bound",
|
||||
AttributeStructure = AttributeStructure.DoubleQuotes,
|
||||
BoundAttribute = IntIndexerTagHelper.BoundAttributes.Single(),
|
||||
Field = "__InputTagHelper",
|
||||
IsIndexerNameMatch = true,
|
||||
Property = "IntIndexer",
|
||||
TagHelper = IntIndexerTagHelper,
|
||||
Source = Span,
|
||||
Children =
|
||||
{
|
||||
new CSharpExpressionIntermediateNode()
|
||||
{
|
||||
Children = { new IntermediateToken { Kind = IntermediateToken.TokenKind.CSharp, Content = "32", } },
|
||||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node1);
|
||||
tagHelperNode.Children.Add(node2);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node2);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.Builder.ToString();
|
||||
Assert.Equal(
|
||||
@"#line 3 ""test.cshtml""
|
||||
__InputTagHelper.IntIndexer[""bound""] = 32;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute(""foo-bound"", __InputTagHelper.IntIndexer[""bound""], global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
|
||||
|
|
@ -744,6 +954,7 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""foo-bound"", __InputTagHelpe
|
|||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new DefaultTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "foo-bound",
|
||||
|
|
@ -761,6 +972,8 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""foo-bound"", __InputTagHelpe
|
|||
}
|
||||
}
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -785,7 +998,7 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""foo-bound"", __InputTagHelpe
|
|||
// Arrange
|
||||
var extension = new DefaultTagHelperTargetExtension() { DesignTime = true };
|
||||
var context = TestCodeRenderingContext.CreateDesignTime();
|
||||
|
||||
|
||||
var node = new DefaultTagHelperRuntimeIntermediateNode();
|
||||
|
||||
// Act
|
||||
|
|
@ -805,7 +1018,7 @@ __tagHelperExecutionContext.AddTagHelperAttribute(""foo-bound"", __InputTagHelpe
|
|||
// Arrange
|
||||
var extension = new DefaultTagHelperTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
|
||||
var node = new DefaultTagHelperRuntimeIntermediateNode();
|
||||
|
||||
// Act
|
||||
|
|
@ -837,6 +1050,11 @@ private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeMana
|
|||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
private static void Push(CodeRenderingContext context, TagHelperIntermediateNode node)
|
||||
{
|
||||
((DefaultCodeRenderingContext)context).AncestorsInternal.Push(node);
|
||||
}
|
||||
|
||||
private static DocumentIntermediateNode Lower(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var engine = RazorEngine.Create();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
|
|
@ -9,7 +10,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
public class PreallocatedAttributeTargetExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void WriteDeclarePreallocatedTagHelperHtmlAttribute_RendersCorrectly()
|
||||
public void WriteTagHelperHtmlAttributeValue_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
|
|
@ -36,7 +37,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDeclarePreallocatedTagHelperHtmlAttribute_Minimized_RendersCorrectly()
|
||||
public void WriteTagHelperHtmlAttributeValue_Minimized_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
|
|
@ -63,16 +64,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteAddPreallocatedTagHelperHtmlAttribute_RendersCorrectly()
|
||||
public void WriteTagHelperHtmlAttribute_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new PreallocatedTagHelperHtmlAttributeIntermediateNode()
|
||||
{
|
||||
VariableName = "_tagHelper1"
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperHtmlAttribute(context, node);
|
||||
|
|
@ -87,12 +91,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteDeclarePreallocatedTagHelperAttribute_RendersCorrectly()
|
||||
public void WriteTagHelperPropertyValue_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
|
||||
var node = new PreallocatedTagHelperPropertyValueIntermediateNode()
|
||||
{
|
||||
AttributeName = "Foo",
|
||||
|
|
@ -114,7 +118,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSetPreallocatedTagHelperProperty_RendersCorrectly()
|
||||
public void WriteTagHelperProperty_RendersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
|
|
@ -124,7 +128,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
tagHelperBuilder.TypeName("FooTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
|
||||
builder
|
||||
.Name("Foo")
|
||||
.TypeName("System.String")
|
||||
|
|
@ -132,13 +135,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new PreallocatedTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = descriptor.Name,
|
||||
Field = "__FooTagHelper",
|
||||
VariableName = "_tagHelper1",
|
||||
BoundAttribute = descriptor,
|
||||
Field = "__FooTagHelper",
|
||||
Property = "FooProp",
|
||||
VariableName = "_tagHelper1",
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -164,7 +171,6 @@ __tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1);
|
|||
tagHelperBuilder.TypeName("FooTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
|
||||
builder
|
||||
.Name("Foo")
|
||||
.TypeName("System.Collections.Generic.Dictionary<System.String, System.String>")
|
||||
|
|
@ -173,6 +179,7 @@ __tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1);
|
|||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node = new PreallocatedTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "pre-Foo",
|
||||
|
|
@ -180,8 +187,11 @@ __tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1);
|
|||
VariableName = "_tagHelper1",
|
||||
BoundAttribute = descriptor,
|
||||
IsIndexerNameMatch = true,
|
||||
Property = "FooProp",
|
||||
TagHelper = tagHelperBuilder.Build(),
|
||||
};
|
||||
tagHelperNode.Children.Add(node);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node);
|
||||
|
|
@ -199,5 +209,68 @@ __tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1);
|
|||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteSetPreallocatedTagHelperProperty_IndexerAttribute_MultipleValues()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new PreallocatedAttributeTargetExtension();
|
||||
var context = TestCodeRenderingContext.CreateRuntime();
|
||||
|
||||
var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test");
|
||||
tagHelperBuilder.TypeName("FooTagHelper");
|
||||
|
||||
var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind);
|
||||
builder
|
||||
.Name("Foo")
|
||||
.TypeName("System.Collections.Generic.Dictionary<System.String, System.String>")
|
||||
.AsDictionaryAttribute("pre-", "System.String")
|
||||
.PropertyName("FooProp");
|
||||
|
||||
var boundAttribute = builder.Build();
|
||||
var tagHelper = tagHelperBuilder.Build();
|
||||
|
||||
var tagHelperNode = new TagHelperIntermediateNode();
|
||||
var node1 = new PreallocatedTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "pre-Bar",
|
||||
Field = "__FooTagHelper",
|
||||
VariableName = "_tagHelper0s",
|
||||
BoundAttribute = boundAttribute,
|
||||
IsIndexerNameMatch = true,
|
||||
Property = "FooProp",
|
||||
TagHelper = tagHelper,
|
||||
};
|
||||
var node2 = new PreallocatedTagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = "pre-Foo",
|
||||
Field = "__FooTagHelper",
|
||||
VariableName = "_tagHelper1",
|
||||
BoundAttribute = boundAttribute,
|
||||
IsIndexerNameMatch = true,
|
||||
Property = "FooProp",
|
||||
TagHelper = tagHelper,
|
||||
};
|
||||
tagHelperNode.Children.Add(node1);
|
||||
tagHelperNode.Children.Add(node2);
|
||||
Push(context, tagHelperNode);
|
||||
|
||||
// Act
|
||||
extension.WriteTagHelperProperty(context, node2);
|
||||
|
||||
// Assert
|
||||
var csharp = context.CodeWriter.Builder.ToString();
|
||||
Assert.Equal(
|
||||
@"__FooTagHelper.FooProp[""Foo""] = (string)_tagHelper1.Value;
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1);
|
||||
",
|
||||
csharp,
|
||||
ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
private static void Push(CodeRenderingContext context, TagHelperIntermediateNode node)
|
||||
{
|
||||
((DefaultCodeRenderingContext)context).AncestorsInternal.Push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,10 @@ __TestNamespace_InputTagHelper1.IntDictionaryProperty["pepper"] = 98;
|
|||
__tagHelperExecutionContext.AddTagHelperAttribute(__tagHelperAttribute_5);
|
||||
__TestNamespace_InputTagHelper2.StringDictionaryProperty["paprika"] = (string)__tagHelperAttribute_5.Value;
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute(__tagHelperAttribute_5);
|
||||
if (__TestNamespace_InputTagHelper1.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(InvalidTagHelperIndexerAssignment("string-prefix-cumin", "TestNamespace.InputTagHelper1", "StringDictionaryProperty"));
|
||||
}
|
||||
BeginWriteTagHelperAttribute();
|
||||
WriteLiteral("literate ");
|
||||
#line 21 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/PrefixedAttributeTagHelpers.cshtml"
|
||||
|
|
@ -192,6 +196,10 @@ __TestNamespace_InputTagHelper1.IntDictionaryProperty["pepper"] = 98;
|
|||
__tagHelperStringValueBuffer = EndWriteTagHelperAttribute();
|
||||
__TestNamespace_InputTagHelper1.StringDictionaryProperty["cumin"] = __tagHelperStringValueBuffer;
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-cumin", __TestNamespace_InputTagHelper1.StringDictionaryProperty["cumin"], global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
|
||||
if (__TestNamespace_InputTagHelper2.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(InvalidTagHelperIndexerAssignment("string-prefix-cumin", "TestNamespace.InputTagHelper2", "StringDictionaryProperty"));
|
||||
}
|
||||
__TestNamespace_InputTagHelper2.StringDictionaryProperty["cumin"] = __TestNamespace_InputTagHelper1.StringDictionaryProperty["cumin"];
|
||||
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
if (!__tagHelperExecutionContext.Output.IsContentModified)
|
||||
|
|
|
|||
Loading…
Reference in New Issue