129 lines
4.7 KiB
C#
129 lines
4.7 KiB
C#
// 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.Collections.Generic;
|
|
using System.Globalization;
|
|
using Microsoft.AspNetCore.Razor.Language;
|
|
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|
{
|
|
public class InstrumentationPass : RazorIRPassBase, IRazorIROptimizationPass
|
|
{
|
|
public override int Order => DefaultFeatureOrder;
|
|
|
|
public override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
|
{
|
|
var walker = new Visitor();
|
|
walker.VisitDocument(irDocument);
|
|
|
|
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 CSharpStatementIRNode()
|
|
{
|
|
Parent = item.Node.Parent
|
|
};
|
|
RazorIRBuilder.Create(beginNode)
|
|
.Add(new RazorIRToken()
|
|
{
|
|
Kind = RazorIRToken.TokenKind.CSharp,
|
|
Content = string.Format("{0}({1}, {2}, {3});",
|
|
beginContextMethodName,
|
|
item.Source.AbsoluteIndex.ToString(CultureInfo.InvariantCulture),
|
|
item.Source.Length.ToString(CultureInfo.InvariantCulture),
|
|
item.IsLiteral ? "true" : "false")
|
|
});
|
|
|
|
var endNode = new CSharpStatementIRNode()
|
|
{
|
|
Parent = item.Node.Parent
|
|
};
|
|
RazorIRBuilder.Create(endNode)
|
|
.Add(new RazorIRToken()
|
|
{
|
|
Kind = RazorIRToken.TokenKind.CSharp,
|
|
Content = string.Format("{0}();", endContextMethodName)
|
|
});
|
|
|
|
var nodeIndex = item.Node.Parent.Children.IndexOf(item.Node);
|
|
item.Node.Parent.Children.Insert(nodeIndex, beginNode);
|
|
item.Node.Parent.Children.Insert(nodeIndex + 2, endNode);
|
|
}
|
|
|
|
private struct InstrumentationItem
|
|
{
|
|
public InstrumentationItem(RazorIRNode node, bool isLiteral, SourceSpan source)
|
|
{
|
|
Node = node;
|
|
IsLiteral = isLiteral;
|
|
Source = source;
|
|
}
|
|
|
|
public RazorIRNode Node { get; }
|
|
|
|
public bool IsLiteral { get; }
|
|
|
|
public SourceSpan Source { get; }
|
|
}
|
|
|
|
private class Visitor : RazorIRNodeWalker
|
|
{
|
|
public List<InstrumentationItem> Items { get; } = new List<InstrumentationItem>();
|
|
|
|
public override void VisitHtml(HtmlContentIRNode node)
|
|
{
|
|
if (node.Source != null)
|
|
{
|
|
Items.Add(new InstrumentationItem(node, isLiteral: true, source: node.Source.Value));
|
|
}
|
|
|
|
VisitDefault(node);
|
|
}
|
|
|
|
public override void VisitCSharpExpression(CSharpExpressionIRNode node)
|
|
{
|
|
if (node.Source != null && !(node.Parent is CSharpAttributeValueIRNode))
|
|
{
|
|
Items.Add(new InstrumentationItem(node, isLiteral: false, source: node.Source.Value));
|
|
}
|
|
|
|
VisitDefault(node);
|
|
}
|
|
|
|
public override void VisitExecuteTagHelpers(ExecuteTagHelpersIRNode node)
|
|
{
|
|
// As a special case the TagHelperIRNode (which must be the parent) is the one that carries
|
|
// the location. The execute node won't have one, but the instrumentation goes around the call
|
|
// to execute.
|
|
if (node.Parent is TagHelperIRNode && node.Parent.Source != null)
|
|
{
|
|
Items.Add(new InstrumentationItem(node, isLiteral: false, source: node.Parent.Source.Value));
|
|
}
|
|
|
|
VisitDefault(node);
|
|
}
|
|
|
|
public override void VisitAddTagHelperHtmlAttribute(AddTagHelperHtmlAttributeIRNode node)
|
|
{
|
|
// We don't want to instrument TagHelper attributes. Do nothing.
|
|
}
|
|
|
|
public override void VisitSetTagHelperProperty(SetTagHelperPropertyIRNode node)
|
|
{
|
|
// We don't want to instrument TagHelper attributes. Do nothing.
|
|
}
|
|
}
|
|
}
|
|
}
|