File: Mvc.Version2_X\InstrumentationPass.cs
Web Access
Project: src\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.Collections.Generic;
using System.Globalization;
using System.Threading;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
 
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X;
 
public sealed class InstrumentationPass : IntermediateNodePassBase, IRazorOptimizationPass
{
    public override int Order => DefaultFeatureOrder;
 
    protected override void ExecuteCore(
        RazorCodeDocument codeDocument,
        DocumentIntermediateNode documentNode,
        CancellationToken cancellationToken)
    {
        if (documentNode.Options.DesignTime)
        {
            return;
        }
 
        var walker = new Visitor();
        walker.VisitDocument(documentNode);
 
        for (var i = 0; i < walker.Items.Count; i++)
        {
            var node = walker.Items[i];
 
            AddInstrumentation(node);
        }
    }
 
    private static void AddInstrumentation(InstrumentationItem item)
    {
        var beginContextMethodName = "BeginContext"; // ORIGINAL: BeginContextMethodName
        var endContextMethodName = "EndContext"; // ORIGINAL: EndContextMethodName
 
        var beginNode = new CSharpCodeIntermediateNode();
 
        var absoluteIndex = item.Source.AbsoluteIndex.ToString(CultureInfo.InvariantCulture);
        var length = item.Source.Length.ToString(CultureInfo.InvariantCulture);
        var isLiteral = item.IsLiteral ? "true" : "false";
 
        beginNode.Children.Add(
            IntermediateNodeFactory.CSharpToken($"{beginContextMethodName}({absoluteIndex}, {length}, {isLiteral});"));
 
        var endNode = new CSharpCodeIntermediateNode();
        endNode.Children.Add(IntermediateNodeFactory.CSharpToken($"{endContextMethodName}();"));
 
        var nodeIndex = item.Parent.Children.IndexOf(item.Node);
        item.Parent.Children.Insert(nodeIndex, beginNode);
        item.Parent.Children.Insert(nodeIndex + 2, endNode);
    }
 
    private struct InstrumentationItem
    {
        public InstrumentationItem(IntermediateNode node, IntermediateNode parent, bool isLiteral, SourceSpan source)
        {
            Node = node;
            Parent = parent;
            IsLiteral = isLiteral;
            Source = source;
        }
 
        public IntermediateNode Node { get; }
 
        public IntermediateNode Parent { get; }
 
        public bool IsLiteral { get; }
 
        public SourceSpan Source { get; }
    }
 
    private class Visitor : IntermediateNodeWalker
    {
        public List<InstrumentationItem> Items { get; } = new List<InstrumentationItem>();
 
        public override void VisitHtml(HtmlContentIntermediateNode node)
        {
            if (node.Source != null)
            {
                Items.Add(new InstrumentationItem(node, Parent!, isLiteral: true, source: node.Source.Value));
            }
 
            VisitDefault(node);
        }
 
        public override void VisitCSharpExpression(CSharpExpressionIntermediateNode node)
        {
            if (node.Source != null)
            {
                Items.Add(new InstrumentationItem(node, Parent!, isLiteral: false, source: node.Source.Value));
            }
 
            VisitDefault(node);
        }
 
        public override void VisitTagHelper(TagHelperIntermediateNode node)
        {
            if (node.Source != null)
            {
                Items.Add(new InstrumentationItem(node, Parent!, isLiteral: false, source: node.Source.Value));
            }
 
            // Inside a tag helper we only want to visit inside of the body (skip all of the attributes and properties).
            for (var i = 0; i < node.Children.Count; i++)
            {
                var child = node.Children[i];
                if (child is TagHelperBodyIntermediateNode ||
                    child is DefaultTagHelperBodyIntermediateNode)
                {
                    VisitDefault(child);
                }
            }
        }
    }
}