diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/SyntaxFactory.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/SyntaxFactory.cs index 6f60cc6b41..8352a2bb1a 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/SyntaxFactory.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/SyntaxFactory.cs @@ -9,9 +9,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax { internal static SyntaxToken Token(SyntaxKind kind, string content, params RazorDiagnostic[] diagnostics) { - if (kind == SyntaxKind.Whitespace && diagnostics.Length == 0) + if (SyntaxTokenCache.CanBeCached(kind, diagnostics)) { - return WhitespaceTokenCache.GetToken(content); + return SyntaxTokenCache.GetCachedToken(kind, content); } return new SyntaxToken(kind, content, diagnostics); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/SyntaxTokenCache.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/SyntaxTokenCache.cs new file mode 100644 index 0000000000..65d1334811 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/SyntaxTokenCache.cs @@ -0,0 +1,74 @@ +// 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 Microsoft.Extensions.Internal; + +namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax +{ + // Simplified version of Roslyn's SyntaxNodeCache + internal static class SyntaxTokenCache + { + private const int CacheSizeBits = 16; + private const int CacheSize = 1 << CacheSizeBits; + private const int CacheMask = CacheSize - 1; + private static readonly Entry[] s_cache = new Entry[CacheSize]; + + private struct Entry + { + public int Hash { get; } + public SyntaxToken Token { get; } + + internal Entry(int hash, SyntaxToken token) + { + Hash = hash; + Token = token; + } + } + + public static bool CanBeCached(SyntaxKind kind, params RazorDiagnostic[] diagnostics) + { + if (diagnostics.Length == 0) + { + switch (kind) + { + case SyntaxKind.CharacterLiteral: + case SyntaxKind.Dot: + case SyntaxKind.Identifier: + case SyntaxKind.IntegerLiteral: + case SyntaxKind.Keyword: + case SyntaxKind.NewLine: + case SyntaxKind.RazorCommentStar: + case SyntaxKind.RazorCommentTransition: + case SyntaxKind.StringLiteral: + case SyntaxKind.Transition: + case SyntaxKind.Whitespace: + return true; + } + } + + return false; + } + + public static SyntaxToken GetCachedToken(SyntaxKind kind, string content) + { + var hash = (kind, content).GetHashCode(); + + // Allow the upper 16 bits to contribute to the index + var indexableHash = hash ^ (hash >> 16); + + var idx = indexableHash & CacheMask; + var e = s_cache[idx]; + + if (e.Hash == hash && e.Token.Kind == kind && e.Token.Content == content) + { + return e.Token; + } + + var token = new SyntaxToken(kind, content, Array.Empty()); + s_cache[idx] = new Entry(hash, token); + + return token; + } + } +} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/WhitespaceTokenCache.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/WhitespaceTokenCache.cs deleted file mode 100644 index f7fede8aa7..0000000000 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Syntax/InternalSyntax/WhitespaceTokenCache.cs +++ /dev/null @@ -1,46 +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; - -namespace Microsoft.AspNetCore.Razor.Language.Syntax.InternalSyntax -{ - // Simplified version of Roslyn's SyntaxNodeCache - internal static class WhitespaceTokenCache - { - private const int CacheSizeBits = 8; - private const int CacheSize = 1 << CacheSizeBits; - private const int CacheMask = CacheSize - 1; - private static readonly Entry[] s_cache = new Entry[CacheSize]; - - private struct Entry - { - public int Hash { get; } - public SyntaxToken Token { get; } - - internal Entry(int hash, SyntaxToken token) - { - Hash = hash; - Token = token; - } - } - - public static SyntaxToken GetToken(string content) - { - var hash = content.GetHashCode(); - - var idx = hash & CacheMask; - var e = s_cache[idx]; - - if (e.Hash == hash && e.Token?.Content == content) - { - return e.Token; - } - - var token = new SyntaxToken(SyntaxKind.Whitespace, content, Array.Empty()); - s_cache[idx] = new Entry(hash, token); - - return token; - } - } -}