| File: Language\Extensions\MetadataAttributePass.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; using System.Threading; using Microsoft.AspNetCore.Razor.Language.Components; using Microsoft.AspNetCore.Razor.Language.Intermediate; namespace Microsoft.AspNetCore.Razor.Language.Extensions; // Optimization pass is the best choice for this class. It's not an optimization, but it also doesn't add semantically // meaningful information. internal sealed class MetadataAttributePass : IntermediateNodePassBase, IRazorOptimizationPass { private IMetadataIdentifierFeature? _identifierFeature; protected override void OnInitialized() { Engine.TryGetFeature(out _identifierFeature); Debug.Assert(_identifierFeature is not null); } protected override void ExecuteCore( RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode, CancellationToken cancellationToken) { if (documentNode.Options == null || documentNode.Options.SuppressMetadataAttributes) { // Metadata attributes are turned off (or options not populated), nothing to do. return; } if (string.Equals(documentNode.DocumentKind, ComponentDocumentClassifierPass.ComponentDocumentKind, StringComparison.Ordinal)) { // Metadata attributes are not used for components. return; } // We need to be able to compute the data we need for the [RazorCompiledItem] attribute - that includes // a full type name, and a document kind, and optionally an identifier. // // If we can't use [RazorCompiledItem] then we don't care about the rest of the attributes. var @namespace = documentNode.FindPrimaryNamespace(); if (@namespace == null) { // No namespace node. Skip. return; } var @class = documentNode.FindPrimaryClass(); if (@class == null || string.IsNullOrEmpty(@class.Name)) { // No class node or it's incomplete. Skip. return; } if (documentNode.DocumentKind == null) { // No document kind. Skip. return; } var identifier = _identifierFeature?.GetIdentifier(codeDocument, codeDocument.Source); if (identifier == null) { // No identifier. Skip return; } // [RazorCompiledItem] is an [assembly: ... ] attribute, so it needs to be applied at the global scope. documentNode.Children.Insert(0, new RazorCompiledItemAttributeIntermediateNode() { TypeName = string.IsNullOrEmpty(@namespace.Name) ? @class.Name : @namespace.Name + "." + @class.Name, Kind = documentNode.DocumentKind, Identifier = identifier, }); // Now we need to add a [RazorSourceChecksum] for the source and for each import // these are class attributes, so we need to find the insertion point to put them // right before the class. var insert = (int?)null; for (var j = 0; j < @namespace.Children.Count; j++) { if (object.ReferenceEquals(@namespace.Children[j], @class)) { insert = j; break; } } if (insert == null) { // Can't find a place to put the attributes, just bail. return; } if (documentNode.Options.SuppressMetadataSourceChecksumAttributes) { // Checksum attributes are turned off (or options not populated), nothing to do. return; } // Checksum of the main source var checksum = codeDocument.Source.Text.GetChecksum(); var checksumAlgorithm = codeDocument.Source.Text.ChecksumAlgorithm; if (checksum == null || checksum.Length == 0 || checksumAlgorithm is CodeAnalysis.Text.SourceHashAlgorithm.None) { // Don't generate anything unless we have all of the required information. return; } @namespace.Children.Insert((int)insert++, new RazorSourceChecksumAttributeIntermediateNode() { Checksum = checksum, ChecksumAlgorithm = checksumAlgorithm, Identifier = identifier, }); // Now process the checksums of the imports Debug.Assert(_identifierFeature != null); foreach (var import in codeDocument.Imports) { checksum = import.Text.GetChecksum(); checksumAlgorithm = import.Text.ChecksumAlgorithm; identifier = _identifierFeature.GetIdentifier(codeDocument, import); if (checksum == null || checksum.Length == 0 || checksumAlgorithm == CodeAnalysis.Text.SourceHashAlgorithm.None || identifier == null) { // It's ok to skip an import if we don't have all of the required information. continue; } @namespace.Children.Insert((int)insert++, new RazorSourceChecksumAttributeIntermediateNode() { Checksum = checksum, ChecksumAlgorithm = checksumAlgorithm, Identifier = identifier, }); } } }