Small perf fixes to improve precompilation times

This commit is contained in:
Pranav K 2016-08-29 17:34:50 -07:00
parent d19845730f
commit 5944dd6f87
8 changed files with 168 additions and 118 deletions

View File

@ -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>

View File

@ -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)

View File

@ -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();
}

View File

@ -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)

View File

@ -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)
{

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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)