Merge pull request #22877 from dotnet/dev/toddgrun/MorePerfOptimizations
Dev/toddgrun/more perf optimizations
This commit is contained in:
commit
073cd0aa40
|
|
@ -554,13 +554,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.StringLiteral));
|
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.StringLiteral));
|
||||||
}
|
}
|
||||||
|
|
||||||
private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', SyntaxKind.CharacterLiteral);
|
private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', IsEndQuotedCharacterLiteral, SyntaxKind.CharacterLiteral);
|
||||||
|
|
||||||
private StateResult QuotedStringLiteral() => QuotedLiteral('\"', SyntaxKind.StringLiteral);
|
private StateResult QuotedStringLiteral() => QuotedLiteral('\"', IsEndQuotedStringLiteral, SyntaxKind.StringLiteral);
|
||||||
|
|
||||||
private StateResult QuotedLiteral(char quote, SyntaxKind literalType)
|
private Func<char, bool> IsEndQuotedCharacterLiteral = (c) => c == '\\' || c == '\'' || ParserHelpers.IsNewLine(c);
|
||||||
|
private Func<char, bool> IsEndQuotedStringLiteral = (c) => c == '\\' || c == '\"' || ParserHelpers.IsNewLine(c);
|
||||||
|
|
||||||
|
private StateResult QuotedLiteral(char quote, Func<char, bool> isEndQuotedLiteral, SyntaxKind literalType)
|
||||||
{
|
{
|
||||||
TakeUntil(c => c == '\\' || c == quote || ParserHelpers.IsNewLine(c));
|
TakeUntil(isEndQuotedLiteral);
|
||||||
if (CurrentCharacter == '\\')
|
if (CurrentCharacter == '\\')
|
||||||
{
|
{
|
||||||
TakeCurrent(); // Take the '\'
|
TakeCurrent(); // Take the '\'
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
private readonly IList<TextLine> _lines;
|
private readonly IList<TextLine> _lines;
|
||||||
private readonly string _filePath;
|
private readonly string _filePath;
|
||||||
private TextLine _currentLine;
|
private TextLine _currentLine;
|
||||||
private TextLine _endLine;
|
|
||||||
|
|
||||||
public LineTrackingStringBuffer(string content, string filePath)
|
public LineTrackingStringBuffer(string content, string filePath)
|
||||||
: this(content.ToCharArray(), filePath)
|
: this(content.ToCharArray(), filePath)
|
||||||
|
|
@ -22,17 +21,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
public LineTrackingStringBuffer(char[] content, string filePath)
|
public LineTrackingStringBuffer(char[] content, string filePath)
|
||||||
{
|
{
|
||||||
_endLine = new TextLine(0, 0);
|
_lines = new List<TextLine>();
|
||||||
_lines = new List<TextLine>() { _endLine };
|
|
||||||
|
|
||||||
Append(content);
|
BuildTextLines(content);
|
||||||
|
|
||||||
_filePath = filePath;
|
_filePath = filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Length
|
public int Length
|
||||||
{
|
{
|
||||||
get { return _endLine.End; }
|
get { return _lines[_lines.Count - 1].End; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceLocation EndLocation
|
public SourceLocation EndLocation
|
||||||
|
|
@ -43,7 +41,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
public CharacterReference CharAt(int absoluteIndex)
|
public CharacterReference CharAt(int absoluteIndex)
|
||||||
{
|
{
|
||||||
var line = FindLine(absoluteIndex);
|
var line = FindLine(absoluteIndex);
|
||||||
if (line == null)
|
if (line.IsDefault)
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(absoluteIndex));
|
throw new ArgumentOutOfRangeException(nameof(absoluteIndex));
|
||||||
}
|
}
|
||||||
|
|
@ -51,38 +49,38 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
return new CharacterReference(line.Content[idx], new SourceLocation(_filePath, absoluteIndex, line.Index, idx));
|
return new CharacterReference(line.Content[idx], new SourceLocation(_filePath, absoluteIndex, line.Index, idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Append(char[] content)
|
private void BuildTextLines(char[] content)
|
||||||
{
|
{
|
||||||
|
string lineText;
|
||||||
|
var lineStart = 0;
|
||||||
|
|
||||||
for (int i = 0; i < content.Length; i++)
|
for (int i = 0; i < content.Length; i++)
|
||||||
{
|
{
|
||||||
AppendCore(content[i]);
|
if (ParserHelpers.IsNewLine(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])))
|
|
||||||
{
|
{
|
||||||
PushNewLine();
|
// \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')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lineText = new string(content, lineStart, (i - lineStart) + 1); // +1 to include the current char
|
||||||
|
_lines.Add(new TextLine(lineStart, _lines.Count, lineText));
|
||||||
|
|
||||||
|
lineStart = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void PushNewLine()
|
lineText = new string(content, lineStart, content.Length - lineStart); // no +1 as content.Length points past the last char already
|
||||||
{
|
_lines.Add(new TextLine(lineStart, _lines.Count, lineText));
|
||||||
_endLine = new TextLine(_endLine.End, _endLine.Index + 1);
|
|
||||||
_lines.Add(_endLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AppendCore(char chr)
|
|
||||||
{
|
|
||||||
Debug.Assert(_lines.Count > 0);
|
|
||||||
_lines[_lines.Count - 1].Content.Append(chr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextLine FindLine(int absoluteIndex)
|
private TextLine FindLine(int absoluteIndex)
|
||||||
{
|
{
|
||||||
TextLine selected = null;
|
TextLine selected;
|
||||||
|
|
||||||
if (_currentLine == null)
|
if (_currentLine.IsDefault)
|
||||||
{
|
{
|
||||||
// Scan from line 0
|
// Scan from line 0
|
||||||
selected = ScanLines(absoluteIndex, 0, _lines.Count);
|
selected = ScanLines(absoluteIndex, 0, _lines.Count);
|
||||||
|
|
@ -104,6 +102,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
selected = ScanLines(absoluteIndex, _currentLine.Index, _lines.Count);
|
selected = ScanLines(absoluteIndex, _currentLine.Index, _lines.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selected = default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (absoluteIndex < _currentLine.Start)
|
else if (absoluteIndex < _currentLine.Start)
|
||||||
{
|
{
|
||||||
|
|
@ -122,6 +124,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
selected = ScanLines(absoluteIndex, 0, _currentLine.Index);
|
selected = ScanLines(absoluteIndex, 0, _currentLine.Index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selected = default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -129,7 +135,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
selected = _currentLine;
|
selected = _currentLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Assert(selected == null || selected.Contains(absoluteIndex));
|
Debug.Assert(selected.IsDefault || selected.Contains(absoluteIndex));
|
||||||
_currentLine = selected;
|
_currentLine = selected;
|
||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +165,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct CharacterReference
|
internal struct CharacterReference
|
||||||
|
|
@ -175,28 +181,26 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
public SourceLocation Location { get; }
|
public SourceLocation Location { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TextLine
|
private struct TextLine
|
||||||
{
|
{
|
||||||
private StringBuilder _content = new StringBuilder();
|
public TextLine(int start, int index, string content)
|
||||||
|
|
||||||
public TextLine(int start, int index)
|
|
||||||
{
|
{
|
||||||
Start = start;
|
Start = start;
|
||||||
Index = index;
|
Index = index;
|
||||||
|
Content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringBuilder Content
|
public string Content { get; }
|
||||||
{
|
|
||||||
get { return _content; }
|
public bool IsDefault => Content == null;
|
||||||
}
|
|
||||||
|
|
||||||
public int Length
|
public int Length
|
||||||
{
|
{
|
||||||
get { return Content.Length; }
|
get { return Content.Length; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Start { get; set; }
|
public int Start { get; }
|
||||||
public int Index { get; set; }
|
public int Index { get; }
|
||||||
|
|
||||||
public int End
|
public int End
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,25 +16,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
private readonly TokenizerView<TTokenizer> _tokenizer;
|
private readonly TokenizerView<TTokenizer> _tokenizer;
|
||||||
private SyntaxListBuilder<SyntaxToken>? _tokenBuilder;
|
private SyntaxListBuilder<SyntaxToken>? _tokenBuilder;
|
||||||
|
|
||||||
// Following four high traffic methods cached as using method groups would cause allocation on every invocation.
|
// Following four high traffic methods cached as using method groups would cause allocation on every invocation.
|
||||||
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
|
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
|
||||||
{
|
{
|
||||||
return token.Kind == SyntaxKind.Whitespace;
|
return token.Kind == SyntaxKind.Whitespace;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
|
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
|
||||||
{
|
{
|
||||||
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
|
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
|
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
|
||||||
{
|
{
|
||||||
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
|
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
|
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
|
||||||
{
|
{
|
||||||
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
|
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer> language, ParserContext context)
|
protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer> language, ParserContext context)
|
||||||
|
|
@ -224,7 +224,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
protected internal bool NextIs(SyntaxKind type)
|
protected internal bool NextIs(SyntaxKind type)
|
||||||
{
|
{
|
||||||
return NextIs(token => token != null && type == token.Kind);
|
// Duplicated logic with NextIs(Func...) to prevent allocation
|
||||||
|
var cur = CurrentToken;
|
||||||
|
var result = false;
|
||||||
|
if (NextToken())
|
||||||
|
{
|
||||||
|
result = (type == CurrentToken.Kind);
|
||||||
|
PutCurrentBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
PutBack(cur);
|
||||||
|
EnsureCurrent();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected internal bool NextIs(params SyntaxKind[] types)
|
protected internal bool NextIs(params SyntaxKind[] types)
|
||||||
|
|
@ -235,21 +247,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
protected internal bool NextIs(Func<SyntaxToken, bool> condition)
|
protected internal bool NextIs(Func<SyntaxToken, bool> condition)
|
||||||
{
|
{
|
||||||
var cur = CurrentToken;
|
var cur = CurrentToken;
|
||||||
|
var result = false;
|
||||||
if (NextToken())
|
if (NextToken())
|
||||||
{
|
{
|
||||||
var result = condition(CurrentToken);
|
result = condition(CurrentToken);
|
||||||
PutCurrentBack();
|
PutCurrentBack();
|
||||||
PutBack(cur);
|
|
||||||
EnsureCurrent();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PutBack(cur);
|
|
||||||
EnsureCurrent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
PutBack(cur);
|
||||||
|
EnsureCurrent();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected internal bool Was(SyntaxKind type)
|
protected internal bool Was(SyntaxKind type)
|
||||||
|
|
@ -289,11 +297,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
||||||
|
|
||||||
protected internal IReadOnlyList<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition)
|
protected internal IReadOnlyList<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition)
|
||||||
{
|
{
|
||||||
if (!EnsureCurrent() || !condition(CurrentToken))
|
if (!EnsureCurrent() || !condition(CurrentToken))
|
||||||
{
|
{
|
||||||
return Array.Empty<SyntaxToken>();
|
return Array.Empty<SyntaxToken>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = new List<SyntaxToken>();
|
var result = new List<SyntaxToken>();
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue