Small perf fixes to improve precompilation times
This commit is contained in:
parent
d19845730f
commit
5944dd6f87
|
|
@ -190,21 +190,21 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
|
|||
|
||||
foreach (var name in allowedChildren)
|
||||
{
|
||||
var valid = TryValidateName(
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
var whitespaceError = Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(
|
||||
nameof(RestrictChildrenAttribute),
|
||||
tagHelperName);
|
||||
errorSink.OnError(SourceLocation.Zero, whitespaceError, length: 0);
|
||||
}
|
||||
else if (TryValidateName(
|
||||
name,
|
||||
whitespaceError:
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(
|
||||
nameof(RestrictChildrenAttribute),
|
||||
tagHelperName),
|
||||
characterErrorBuilder: (invalidCharacter) =>
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
|
||||
nameof(RestrictChildrenAttribute),
|
||||
name,
|
||||
tagHelperName,
|
||||
invalidCharacter),
|
||||
errorSink: errorSink);
|
||||
|
||||
if (valid)
|
||||
invalidCharacter => Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
|
||||
nameof(RestrictChildrenAttribute),
|
||||
name,
|
||||
tagHelperName,
|
||||
invalidCharacter),
|
||||
errorSink))
|
||||
{
|
||||
validAllowedChildren.Add(name);
|
||||
}
|
||||
|
|
@ -281,17 +281,29 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
internal static bool ValidateParentTagName(string parentTag, ErrorSink errorSink)
|
||||
{
|
||||
return parentTag == null ||
|
||||
TryValidateName(
|
||||
if (parentTag == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(parentTag))
|
||||
{
|
||||
var error = Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(
|
||||
Resources.TagHelperDescriptorFactory_ParentTag);
|
||||
errorSink.OnError(SourceLocation.Zero, error, length: 0);
|
||||
return false;
|
||||
}
|
||||
else if (!TryValidateName(
|
||||
parentTag,
|
||||
invalidCharacter => Resources.FormatHtmlTargetElementAttribute_InvalidName(
|
||||
Resources.TagHelperDescriptorFactory_ParentTag.ToLower(),
|
||||
parentTag,
|
||||
Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(
|
||||
Resources.TagHelperDescriptorFactory_ParentTag),
|
||||
characterErrorBuilder: (invalidCharacter) =>
|
||||
Resources.FormatHtmlTargetElementAttribute_InvalidName(
|
||||
Resources.TagHelperDescriptorFactory_ParentTag.ToLower(),
|
||||
parentTag,
|
||||
invalidCharacter),
|
||||
errorSink: errorSink);
|
||||
invalidCharacter),
|
||||
errorSink))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryGetRequiredAttributeDescriptors(
|
||||
|
|
@ -320,45 +332,42 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
|
|||
Resources.TagHelperDescriptorFactory_Attribute :
|
||||
Resources.TagHelperDescriptorFactory_Tag;
|
||||
|
||||
var validName = TryValidateName(
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
var error = Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName);
|
||||
errorSink.OnError(SourceLocation.Zero, error, length: 0);
|
||||
return false;
|
||||
}
|
||||
else if (!TryValidateName(
|
||||
name,
|
||||
whitespaceError: Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName),
|
||||
characterErrorBuilder: (invalidCharacter) =>
|
||||
Resources.FormatHtmlTargetElementAttribute_InvalidName(
|
||||
targetName.ToLower(),
|
||||
name,
|
||||
invalidCharacter),
|
||||
errorSink: errorSink);
|
||||
invalidCharacter => Resources.FormatHtmlTargetElementAttribute_InvalidName(
|
||||
targetName.ToLower(),
|
||||
name,
|
||||
invalidCharacter),
|
||||
errorSink))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return validName;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryValidateName(
|
||||
string name,
|
||||
string whitespaceError,
|
||||
Func<char, string> characterErrorBuilder,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var validName = true;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
foreach (var character in name)
|
||||
{
|
||||
errorSink.OnError(SourceLocation.Zero, whitespaceError, length: 0);
|
||||
|
||||
validName = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var character in name)
|
||||
if (char.IsWhiteSpace(character) ||
|
||||
InvalidNonWhitespaceNameCharacters.Contains(character))
|
||||
{
|
||||
if (char.IsWhiteSpace(character) ||
|
||||
InvalidNonWhitespaceNameCharacters.Contains(character))
|
||||
{
|
||||
var error = characterErrorBuilder(character);
|
||||
errorSink.OnError(SourceLocation.Zero, error, length: 0);
|
||||
var error = characterErrorBuilder(character);
|
||||
errorSink.OnError(SourceLocation.Zero, error, length: 0);
|
||||
|
||||
validName = false;
|
||||
}
|
||||
validName = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -696,7 +705,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
|
|||
return property.GetIndexParameters().Length == 0 &&
|
||||
property.GetMethod != null &&
|
||||
property.GetMethod.IsPublic &&
|
||||
property.GetCustomAttribute<HtmlAttributeNotBoundAttribute>(inherit: false) == null;
|
||||
!property.IsDefined(typeof(HtmlAttributeNotBoundAttribute), inherit: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Parser.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
|
||||
{
|
||||
|
|
@ -77,29 +76,34 @@ namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
|
|||
descriptors = matchingDescriptors.Concat(descriptors);
|
||||
}
|
||||
|
||||
var applicableDescriptors = ApplyRequiredAttributes(descriptors, attributes);
|
||||
applicableDescriptors = ApplyParentTagFilter(applicableDescriptors, parentTagName);
|
||||
var applicableDescriptors = new List<TagHelperDescriptor>();
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
if (HasRequiredAttributes(descriptor, attributes) &&
|
||||
HasRequiredParentTag(descriptor, parentTagName))
|
||||
{
|
||||
applicableDescriptors.Add(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return applicableDescriptors.ToArray();
|
||||
return applicableDescriptors;
|
||||
}
|
||||
|
||||
private IEnumerable<TagHelperDescriptor> ApplyParentTagFilter(
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
private bool HasRequiredParentTag(
|
||||
TagHelperDescriptor descriptor,
|
||||
string parentTagName)
|
||||
{
|
||||
return descriptors.Where(descriptor =>
|
||||
descriptor.RequiredParent == null ||
|
||||
string.Equals(parentTagName, descriptor.RequiredParent, StringComparison.OrdinalIgnoreCase));
|
||||
return descriptor.RequiredParent == null ||
|
||||
string.Equals(parentTagName, descriptor.RequiredParent, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private IEnumerable<TagHelperDescriptor> ApplyRequiredAttributes(
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
private bool HasRequiredAttributes(
|
||||
TagHelperDescriptor descriptor,
|
||||
IEnumerable<KeyValuePair<string, string>> attributes)
|
||||
{
|
||||
return descriptors.Where(
|
||||
descriptor => descriptor.RequiredAttributes.All(
|
||||
requiredAttribute => attributes.Any(
|
||||
attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value))));
|
||||
return descriptor.RequiredAttributes.All(
|
||||
requiredAttribute => attributes.Any(
|
||||
attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value)));
|
||||
}
|
||||
|
||||
private void Register(TagHelperDescriptor descriptor)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Chunks.Generators;
|
||||
using Microsoft.AspNetCore.Razor.Editor;
|
||||
using Microsoft.AspNetCore.Razor.Parser.Internal;
|
||||
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNetCore.Razor.Tokenizer.Internal;
|
||||
|
||||
|
|
@ -41,12 +40,19 @@ namespace Microsoft.AspNetCore.Razor.Parser
|
|||
protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block)
|
||||
{
|
||||
// Collect the content of this node
|
||||
var content = string.Concat(block.Children.Cast<Span>().Select(s => s.Content));
|
||||
var builder = new StringBuilder();
|
||||
for (var i = 0; i < block.Children.Count; i++)
|
||||
{
|
||||
var childSpan = (Span)block.Children[i];
|
||||
builder.Append(childSpan.Content);
|
||||
}
|
||||
|
||||
// Create a new span containing this content
|
||||
var span = new SpanBuilder();
|
||||
span.EditHandler = new SpanEditHandler(HtmlTokenizer.Tokenize);
|
||||
FillSpan(span, block.Children.Cast<Span>().First().Start, content);
|
||||
Debug.Assert(block.Children.Count > 0);
|
||||
var start = ((Span)block.Children[0]).Start;
|
||||
FillSpan(span, start, builder.ToString());
|
||||
return span.Build();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Chunks.Generators;
|
||||
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
|
||||
using Microsoft.AspNetCore.Razor.Editor;
|
||||
|
|
@ -207,15 +208,23 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
|
|||
// We're now at: " |asp-for='...'" or " |asp-for=..."
|
||||
// The goal here is to capture the attribute name.
|
||||
|
||||
var symbolContents = htmlSymbols
|
||||
.Skip(i) // Skip prefix
|
||||
.TakeWhile(nameSymbol => HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol))
|
||||
.Select(nameSymbol => nameSymbol.Content);
|
||||
|
||||
var nameBuilder = new StringBuilder();
|
||||
// Move the indexer past the attribute name symbols.
|
||||
i += symbolContents.Count() - 1;
|
||||
for (var j = i; j < htmlSymbols.Length; j++)
|
||||
{
|
||||
var nameSymbol = htmlSymbols[j];
|
||||
if (!HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
name = string.Concat(symbolContents);
|
||||
nameBuilder.Append(nameSymbol.Content);
|
||||
i++;
|
||||
}
|
||||
|
||||
i--;
|
||||
|
||||
name = nameBuilder.ToString();
|
||||
attributeValueStartLocation = SourceLocation.Advance(attributeValueStartLocation, name);
|
||||
}
|
||||
else if (symbol.Type == HtmlSymbolType.Equals)
|
||||
|
|
|
|||
|
|
@ -597,12 +597,14 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
|
|||
private static bool IsPartialTag(Block tagBlock)
|
||||
{
|
||||
// No need to validate the tag end because in order to be a tag block it must start with '<'.
|
||||
var tagEnd = tagBlock.Children.Last() as Span;
|
||||
var tagEnd = tagBlock.Children[tagBlock.Children.Count - 1] as Span;
|
||||
|
||||
// If our tag end is not a markup span it means it's some sort of code SyntaxTreeNode (not a valid format)
|
||||
if (tagEnd != null && tagEnd.Kind == SpanKind.Markup)
|
||||
{
|
||||
var endSymbol = tagEnd.Symbols.LastOrDefault() as HtmlSymbol;
|
||||
var endSymbol = tagEnd.Symbols.Count > 0 ?
|
||||
tagEnd.Symbols[tagEnd.Symbols.Count - 1] as HtmlSymbol :
|
||||
null;
|
||||
|
||||
if (endSymbol != null && endSymbol.Type == HtmlSymbolType.CloseAngle)
|
||||
{
|
||||
|
|
@ -661,8 +663,8 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
|
|||
|
||||
private bool IsPotentialTagHelper(string tagName, Block childBlock)
|
||||
{
|
||||
var child = childBlock.Children.FirstOrDefault();
|
||||
Debug.Assert(child != null);
|
||||
Debug.Assert(childBlock.Children.Count > 0);
|
||||
var child = childBlock.Children[0];
|
||||
|
||||
var childSpan = (Span)child;
|
||||
|
||||
|
|
@ -752,7 +754,18 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
|
|||
}
|
||||
|
||||
var childSpan = (Span)child;
|
||||
var textSymbol = childSpan.Symbols.FirstHtmlSymbolAs(HtmlSymbolType.WhiteSpace | HtmlSymbolType.Text);
|
||||
HtmlSymbol textSymbol = null;
|
||||
for (var i = 0; i < childSpan.Symbols.Count; i++)
|
||||
{
|
||||
var symbol = childSpan.Symbols[i] as HtmlSymbol;
|
||||
|
||||
if (symbol != null &&
|
||||
(symbol.Type & (HtmlSymbolType.WhiteSpace | HtmlSymbolType.Text)) == symbol.Type)
|
||||
{
|
||||
textSymbol = symbol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (textSymbol == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,17 +33,24 @@ namespace Microsoft.AspNetCore.Razor.Text
|
|||
|
||||
public void Append(string content)
|
||||
{
|
||||
for (int i = 0; i < content.Length; i++)
|
||||
var previousIndex = 0;
|
||||
for (var i = 0; i < content.Length; i++)
|
||||
{
|
||||
AppendCore(content[i]);
|
||||
|
||||
// \r on it's own: Start a new line, otherwise wait for \n
|
||||
// Other Newline: Start a new line
|
||||
if ((content[i] == '\r' && (i + 1 == content.Length || content[i + 1] != '\n')) || (content[i] != '\r' && ParserHelpers.IsNewLine(content[i])))
|
||||
if ((content[i] == '\r' && (i + 1 == content.Length || content[i + 1] != '\n')) ||
|
||||
(content[i] != '\r' && ParserHelpers.IsNewLine(content[i])))
|
||||
{
|
||||
AppendCore(content, previousIndex, i - previousIndex + 1);
|
||||
previousIndex = i + 1;
|
||||
PushNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
if (previousIndex < content.Length)
|
||||
{
|
||||
AppendCore(content, previousIndex, content.Length - previousIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public CharacterReference CharAt(int absoluteIndex)
|
||||
|
|
@ -63,6 +70,12 @@ namespace Microsoft.AspNetCore.Razor.Text
|
|||
_lines.Add(_endLine);
|
||||
}
|
||||
|
||||
private void AppendCore(string content, int index, int length)
|
||||
{
|
||||
Debug.Assert(_lines.Count > 0);
|
||||
_lines[_lines.Count - 1].Content.Append(content, index, length);
|
||||
}
|
||||
|
||||
private void AppendCore(char chr)
|
||||
{
|
||||
Debug.Assert(_lines.Count > 0);
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
// 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 System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tokenizer.Symbols.Internal
|
||||
{
|
||||
public static class HtmlSymbolExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the generic <see cref="IEnumerable{ISymbol}"/> to a <see cref="IEnumerable{HtmlSymbol}"/> and
|
||||
/// finds the first <see cref="HtmlSymbol"/> with type <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="symbols">The <see cref="IEnumerable{ISymbol}"/> instance this method extends.</param>
|
||||
/// <param name="type">The <see cref="HtmlSymbolType"/> to search for.</param>
|
||||
/// <returns>The first <see cref="HtmlSymbol"/> of type <paramref name="type"/>.</returns>
|
||||
public static HtmlSymbol FirstHtmlSymbolAs(this IEnumerable<ISymbol> symbols, HtmlSymbolType type)
|
||||
{
|
||||
return symbols.OfType<HtmlSymbol>().FirstOrDefault(sym => (type & sym.Type) == sym.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
|
||||
using Microsoft.AspNetCore.Razor.Text;
|
||||
|
||||
|
|
@ -13,7 +13,21 @@ namespace Microsoft.AspNetCore.Razor.Tokenizer.Symbols
|
|||
{
|
||||
public static LocationTagged<string> GetContent(this SpanBuilder span)
|
||||
{
|
||||
return GetContent(span, e => e);
|
||||
var symbols = span.Symbols;
|
||||
if (symbols.Count > 0)
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
for (var i = 0; i < symbols.Count; i++)
|
||||
{
|
||||
text.Append(symbols[i].Content);
|
||||
}
|
||||
|
||||
return new LocationTagged<string>(text.ToString(), span.Start + symbols[0].Start);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new LocationTagged<string>(string.Empty, span.Start);
|
||||
}
|
||||
}
|
||||
|
||||
public static LocationTagged<string> GetContent(this SpanBuilder span, Func<IEnumerable<ISymbol>, IEnumerable<ISymbol>> filter)
|
||||
|
|
@ -23,14 +37,21 @@ namespace Microsoft.AspNetCore.Razor.Tokenizer.Symbols
|
|||
|
||||
public static LocationTagged<string> GetContent(this IEnumerable<ISymbol> symbols, SourceLocation spanStart)
|
||||
{
|
||||
if (symbols.Any())
|
||||
StringBuilder builder = null;
|
||||
var location = spanStart;
|
||||
|
||||
foreach (var symbol in symbols)
|
||||
{
|
||||
return new LocationTagged<string>(string.Concat(symbols.Select(s => s.Content)), spanStart + symbols.First().Start);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new LocationTagged<string>(string.Empty, spanStart);
|
||||
if (builder == null)
|
||||
{
|
||||
builder = new StringBuilder();
|
||||
location += symbol.Start;
|
||||
}
|
||||
|
||||
builder.Append(symbol.Content);
|
||||
}
|
||||
|
||||
return new LocationTagged<string>(builder?.ToString() ?? string.Empty, location);
|
||||
}
|
||||
|
||||
public static LocationTagged<string> GetContent(this ISymbol symbol)
|
||||
|
|
|
|||
Loading…
Reference in New Issue