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) 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, name,
whitespaceError: invalidCharacter => Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace( nameof(RestrictChildrenAttribute),
nameof(RestrictChildrenAttribute), name,
tagHelperName), tagHelperName,
characterErrorBuilder: (invalidCharacter) => invalidCharacter),
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName( errorSink))
nameof(RestrictChildrenAttribute),
name,
tagHelperName,
invalidCharacter),
errorSink: errorSink);
if (valid)
{ {
validAllowedChildren.Add(name); validAllowedChildren.Add(name);
} }
@ -281,17 +281,29 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
/// </summary> /// </summary>
internal static bool ValidateParentTagName(string parentTag, ErrorSink errorSink) internal static bool ValidateParentTagName(string parentTag, ErrorSink errorSink)
{ {
return parentTag == null || if (parentTag == null)
TryValidateName( {
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, parentTag,
Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace( invalidCharacter),
Resources.TagHelperDescriptorFactory_ParentTag), errorSink))
characterErrorBuilder: (invalidCharacter) => {
Resources.FormatHtmlTargetElementAttribute_InvalidName( return false;
Resources.TagHelperDescriptorFactory_ParentTag.ToLower(), }
parentTag,
invalidCharacter), return true;
errorSink: errorSink);
} }
private static bool TryGetRequiredAttributeDescriptors( private static bool TryGetRequiredAttributeDescriptors(
@ -320,45 +332,42 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
Resources.TagHelperDescriptorFactory_Attribute : Resources.TagHelperDescriptorFactory_Attribute :
Resources.TagHelperDescriptorFactory_Tag; 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, name,
whitespaceError: Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName), invalidCharacter => Resources.FormatHtmlTargetElementAttribute_InvalidName(
characterErrorBuilder: (invalidCharacter) => targetName.ToLower(),
Resources.FormatHtmlTargetElementAttribute_InvalidName( name,
targetName.ToLower(), invalidCharacter),
name, errorSink))
invalidCharacter), {
errorSink: errorSink); return false;
}
return validName; return true;
} }
private static bool TryValidateName( private static bool TryValidateName(
string name, string name,
string whitespaceError,
Func<char, string> characterErrorBuilder, Func<char, string> characterErrorBuilder,
ErrorSink errorSink) ErrorSink errorSink)
{ {
var validName = true; var validName = true;
if (string.IsNullOrWhiteSpace(name)) foreach (var character in name)
{ {
errorSink.OnError(SourceLocation.Zero, whitespaceError, length: 0); if (char.IsWhiteSpace(character) ||
InvalidNonWhitespaceNameCharacters.Contains(character))
validName = false;
}
else
{
foreach (var character in name)
{ {
if (char.IsWhiteSpace(character) || var error = characterErrorBuilder(character);
InvalidNonWhitespaceNameCharacters.Contains(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 && return property.GetIndexParameters().Length == 0 &&
property.GetMethod != null && property.GetMethod != null &&
property.GetMethod.IsPublic && property.GetMethod.IsPublic &&
property.GetCustomAttribute<HtmlAttributeNotBoundAttribute>(inherit: false) == null; !property.IsDefined(typeof(HtmlAttributeNotBoundAttribute), inherit: false);
} }
/// <summary> /// <summary>

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Razor.Parser.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
{ {
@ -77,29 +76,34 @@ namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
descriptors = matchingDescriptors.Concat(descriptors); descriptors = matchingDescriptors.Concat(descriptors);
} }
var applicableDescriptors = ApplyRequiredAttributes(descriptors, attributes); var applicableDescriptors = new List<TagHelperDescriptor>();
applicableDescriptors = ApplyParentTagFilter(applicableDescriptors, parentTagName); foreach (var descriptor in descriptors)
{
if (HasRequiredAttributes(descriptor, attributes) &&
HasRequiredParentTag(descriptor, parentTagName))
{
applicableDescriptors.Add(descriptor);
}
}
return applicableDescriptors.ToArray(); return applicableDescriptors;
} }
private IEnumerable<TagHelperDescriptor> ApplyParentTagFilter( private bool HasRequiredParentTag(
IEnumerable<TagHelperDescriptor> descriptors, TagHelperDescriptor descriptor,
string parentTagName) string parentTagName)
{ {
return descriptors.Where(descriptor => return descriptor.RequiredParent == null ||
descriptor.RequiredParent == null || string.Equals(parentTagName, descriptor.RequiredParent, StringComparison.OrdinalIgnoreCase);
string.Equals(parentTagName, descriptor.RequiredParent, StringComparison.OrdinalIgnoreCase));
} }
private IEnumerable<TagHelperDescriptor> ApplyRequiredAttributes( private bool HasRequiredAttributes(
IEnumerable<TagHelperDescriptor> descriptors, TagHelperDescriptor descriptor,
IEnumerable<KeyValuePair<string, string>> attributes) IEnumerable<KeyValuePair<string, string>> attributes)
{ {
return descriptors.Where( return descriptor.RequiredAttributes.All(
descriptor => descriptor.RequiredAttributes.All( requiredAttribute => attributes.Any(
requiredAttribute => attributes.Any( attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value)));
attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value))));
} }
private void Register(TagHelperDescriptor descriptor) private void Register(TagHelperDescriptor descriptor)

View File

@ -3,10 +3,9 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Text;
using Microsoft.AspNetCore.Razor.Chunks.Generators; using Microsoft.AspNetCore.Razor.Chunks.Generators;
using Microsoft.AspNetCore.Razor.Editor; using Microsoft.AspNetCore.Razor.Editor;
using Microsoft.AspNetCore.Razor.Parser.Internal;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree; using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Tokenizer.Internal; using Microsoft.AspNetCore.Razor.Tokenizer.Internal;
@ -41,12 +40,19 @@ namespace Microsoft.AspNetCore.Razor.Parser
protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block) protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block)
{ {
// Collect the content of this node // 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 // Create a new span containing this content
var span = new SpanBuilder(); var span = new SpanBuilder();
span.EditHandler = new SpanEditHandler(HtmlTokenizer.Tokenize); 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(); return span.Build();
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.Chunks.Generators; using Microsoft.AspNetCore.Razor.Chunks.Generators;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers; using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Editor; 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=..." // We're now at: " |asp-for='...'" or " |asp-for=..."
// The goal here is to capture the attribute name. // The goal here is to capture the attribute name.
var symbolContents = htmlSymbols var nameBuilder = new StringBuilder();
.Skip(i) // Skip prefix
.TakeWhile(nameSymbol => HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol))
.Select(nameSymbol => nameSymbol.Content);
// Move the indexer past the attribute name symbols. // 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); attributeValueStartLocation = SourceLocation.Advance(attributeValueStartLocation, name);
} }
else if (symbol.Type == HtmlSymbolType.Equals) 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) 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 '<'. // 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 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) 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) 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) private bool IsPotentialTagHelper(string tagName, Block childBlock)
{ {
var child = childBlock.Children.FirstOrDefault(); Debug.Assert(childBlock.Children.Count > 0);
Debug.Assert(child != null); var child = childBlock.Children[0];
var childSpan = (Span)child; var childSpan = (Span)child;
@ -752,7 +754,18 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers.Internal
} }
var childSpan = (Span)child; 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) if (textSymbol == null)
{ {

View File

@ -33,17 +33,24 @@ namespace Microsoft.AspNetCore.Razor.Text
public void Append(string content) 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 // \r on it's own: Start a new line, otherwise wait for \n
// Other Newline: Start a new line // 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(); PushNewLine();
} }
} }
if (previousIndex < content.Length)
{
AppendCore(content, previousIndex, content.Length - previousIndex);
}
} }
public CharacterReference CharAt(int absoluteIndex) public CharacterReference CharAt(int absoluteIndex)
@ -63,6 +70,12 @@ namespace Microsoft.AspNetCore.Razor.Text
_lines.Add(_endLine); _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) private void AppendCore(char chr)
{ {
Debug.Assert(_lines.Count > 0); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Text;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree; using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Text; using Microsoft.AspNetCore.Razor.Text;
@ -13,7 +13,21 @@ namespace Microsoft.AspNetCore.Razor.Tokenizer.Symbols
{ {
public static LocationTagged<string> GetContent(this SpanBuilder span) 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) 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) 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); if (builder == null)
} {
else builder = new StringBuilder();
{ location += symbol.Start;
return new LocationTagged<string>(string.Empty, spanStart); }
builder.Append(symbol.Content);
} }
return new LocationTagged<string>(builder?.ToString() ?? string.Empty, location);
} }
public static LocationTagged<string> GetContent(this ISymbol symbol) public static LocationTagged<string> GetContent(this ISymbol symbol)