168 lines
7.8 KiB
C#
168 lines
7.8 KiB
C#
// 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.Diagnostics;
|
|
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
|
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|
|
|
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|
{
|
|
internal class PreallocatedAttributeTargetExtension : IPreallocatedAttributeTargetExtension
|
|
{
|
|
public string TagHelperAttributeTypeName { get; set; } = "Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute";
|
|
|
|
public string EncodedHtmlStringTypeName { get; set; } = "Microsoft.AspNetCore.Html.HtmlString";
|
|
|
|
public string ExecutionContextVariableName { get; set; } = "__tagHelperExecutionContext";
|
|
|
|
public string ExecutionContextAddHtmlAttributeMethodName { get; set; } = "AddHtmlAttribute";
|
|
|
|
public string ExecutionContextAddTagHelperAttributeMethodName { get; set; } = "AddTagHelperAttribute";
|
|
|
|
public string FormatInvalidIndexerAssignmentMethodName { get; set; } = "InvalidTagHelperIndexerAssignment";
|
|
|
|
public void WriteTagHelperHtmlAttributeValue(CodeRenderingContext context, PreallocatedTagHelperHtmlAttributeValueIntermediateNode node)
|
|
{
|
|
context.CodeWriter
|
|
.Write("private static readonly global::")
|
|
.Write(TagHelperAttributeTypeName)
|
|
.Write(" ")
|
|
.Write(node.VariableName)
|
|
.Write(" = ")
|
|
.WriteStartNewObject("global::" + TagHelperAttributeTypeName)
|
|
.WriteStringLiteral(node.AttributeName);
|
|
|
|
if (node.AttributeStructure == AttributeStructure.Minimized)
|
|
{
|
|
context.CodeWriter.WriteEndMethodInvocation();
|
|
}
|
|
else
|
|
{
|
|
context.CodeWriter
|
|
.WriteParameterSeparator()
|
|
.WriteStartNewObject("global::" + EncodedHtmlStringTypeName)
|
|
.WriteStringLiteral(node.Value)
|
|
.WriteEndMethodInvocation(endLine: false)
|
|
.WriteParameterSeparator()
|
|
.Write($"global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.{node.AttributeStructure}")
|
|
.WriteEndMethodInvocation();
|
|
}
|
|
}
|
|
|
|
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)
|
|
.WriteEndMethodInvocation();
|
|
}
|
|
|
|
public void WriteTagHelperPropertyValue(CodeRenderingContext context, PreallocatedTagHelperPropertyValueIntermediateNode node)
|
|
{
|
|
context.CodeWriter
|
|
.Write("private static readonly global::")
|
|
.Write(TagHelperAttributeTypeName)
|
|
.Write(" ")
|
|
.Write(node.VariableName)
|
|
.Write(" = ")
|
|
.WriteStartNewObject("global::" + TagHelperAttributeTypeName)
|
|
.WriteStringLiteral(node.AttributeName)
|
|
.WriteParameterSeparator()
|
|
.WriteStringLiteral(node.Value)
|
|
.WriteParameterSeparator()
|
|
.Write($"global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.{node.AttributeStructure}")
|
|
.WriteEndMethodInvocation();
|
|
}
|
|
|
|
public void WriteTagHelperProperty(CodeRenderingContext context, PreallocatedTagHelperPropertyIntermediateNode node)
|
|
{
|
|
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 &&
|
|
object.ReferenceEquals(FindFirstUseOfIndexer(tagHelperNode, node), node))
|
|
{
|
|
// Throw a reasonable Exception at runtime if the dictionary property is null.
|
|
context.CodeWriter
|
|
.Write("if (")
|
|
.Write(node.FieldName)
|
|
.Write(".")
|
|
.Write(node.PropertyName)
|
|
.WriteLine(" == null)");
|
|
using (context.CodeWriter.BuildScope())
|
|
{
|
|
// System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName
|
|
// of InvalidOperationException type.
|
|
context.CodeWriter
|
|
.Write("throw ")
|
|
.WriteStartNewObject(nameof(InvalidOperationException))
|
|
.WriteStartMethodInvocation(FormatInvalidIndexerAssignmentMethodName)
|
|
.WriteStringLiteral(node.AttributeName)
|
|
.WriteParameterSeparator()
|
|
.WriteStringLiteral(node.TagHelper.GetTypeName())
|
|
.WriteParameterSeparator()
|
|
.WriteStringLiteral(node.PropertyName)
|
|
.WriteEndMethodInvocation(endLine: false) // End of method call
|
|
.WriteEndMethodInvocation(); // End of new expression / throw statement
|
|
}
|
|
}
|
|
|
|
context.CodeWriter
|
|
.WriteStartAssignment(GetPropertyAccessor(node))
|
|
.Write("(string)")
|
|
.Write($"{node.VariableName}.Value")
|
|
.WriteLine(";")
|
|
.WriteStartInstanceMethodInvocation(ExecutionContextVariableName, ExecutionContextAddTagHelperAttributeMethodName)
|
|
.Write(node.VariableName)
|
|
.WriteEndMethodInvocation();
|
|
}
|
|
|
|
private static PreallocatedTagHelperPropertyIntermediateNode FindFirstUseOfIndexer(
|
|
TagHelperIntermediateNode tagHelperNode,
|
|
PreallocatedTagHelperPropertyIntermediateNode 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 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.FieldName}.{node.PropertyName}";
|
|
|
|
if (node.IsIndexerNameMatch)
|
|
{
|
|
var dictionaryKey = node.AttributeName.Substring(node.BoundAttribute.IndexerNamePrefix.Length);
|
|
propertyAccessor += $"[\"{dictionaryKey}\"]";
|
|
}
|
|
|
|
return propertyAccessor;
|
|
}
|
|
}
|
|
}
|