Refactor IR phases
- Added DirectiveRemovalIRPass - Added IRazorDocumentClassifierPhase, IRazorDirectiveClassifierPhase and IRazorIROptimizationPhase - Added all the related passes and default implementations - Refactored DefaultDirectiveIRPass to do the right thing - Execute method in IR passes now return void - Added tests for the new phases
This commit is contained in:
parent
80172c641d
commit
8ac5468714
|
|
@ -148,7 +148,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
Context.Writer.SetIndent(originalIndent);
|
Context.Writer.SetIndent(originalIndent);
|
||||||
}
|
}
|
||||||
Context.Writer.WriteLine("))();");
|
Context.Writer.WriteLine("))();");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void VisitTemplate(TemplateIRNode node)
|
public override void VisitTemplate(TemplateIRNode node)
|
||||||
|
|
|
||||||
|
|
@ -2,42 +2,89 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
internal class DefaultDirectiveIRPass : RazorIRPassBase
|
internal class DefaultDirectiveIRPass : RazorIRPassBase, IRazorDirectiveClassifierPass
|
||||||
{
|
{
|
||||||
public override int Order => RazorIRPass.DefaultDirectiveClassifierOrder;
|
public override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
|
|
||||||
public override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
|
||||||
{
|
{
|
||||||
var parserOptions = irDocument.Options;
|
var parserOptions = irDocument.Options;
|
||||||
|
|
||||||
var designTime = parserOptions.DesignTimeMode;
|
var designTime = parserOptions.DesignTimeMode;
|
||||||
var walker = new DirectiveWalker(designTime);
|
var walker = new DirectiveWalker();
|
||||||
walker.VisitDocument(irDocument);
|
walker.VisitDocument(irDocument);
|
||||||
|
|
||||||
return irDocument;
|
var classNode = walker.ClassNode;
|
||||||
|
foreach (var node in walker.FunctionsDirectiveNodes)
|
||||||
|
{
|
||||||
|
node.Parent.Children.Remove(node);
|
||||||
|
|
||||||
|
foreach (var child in node.Children.Except(node.Tokens))
|
||||||
|
{
|
||||||
|
child.Parent = classNode;
|
||||||
|
classNode.Children.Add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var node in walker.InheritsDirectiveNodes.Reverse())
|
||||||
|
{
|
||||||
|
node.Parent.Children.Remove(node);
|
||||||
|
|
||||||
|
var token = node.Tokens.FirstOrDefault();
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
classNode.BaseType = token.Content;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var node in walker.SectionDirectiveNodes)
|
||||||
|
{
|
||||||
|
var sectionIndex = node.Parent.Children.IndexOf(node);
|
||||||
|
node.Parent.Children.Remove(node);
|
||||||
|
|
||||||
|
var defineSectionEndStatement = new CSharpStatementIRNode()
|
||||||
|
{
|
||||||
|
Content = "});",
|
||||||
|
};
|
||||||
|
node.Parent.Children.Insert(sectionIndex, defineSectionEndStatement);
|
||||||
|
|
||||||
|
foreach (var child in node.Children.Except(node.Tokens).Reverse())
|
||||||
|
{
|
||||||
|
node.Parent.Children.Insert(sectionIndex, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
var lambdaContent = designTime ? "__razor_section_writer" : string.Empty;
|
||||||
|
var sectionName = node.Tokens.FirstOrDefault()?.Content;
|
||||||
|
var defineSectionStartStatement = new CSharpStatementIRNode()
|
||||||
|
{
|
||||||
|
Content = /* ORIGINAL: DefineSectionMethodName */ $"DefineSection(\"{sectionName}\", async ({lambdaContent}) => {{",
|
||||||
|
};
|
||||||
|
|
||||||
|
node.Parent.Children.Insert(sectionIndex, defineSectionStartStatement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DirectiveWalker : RazorIRNodeWalker
|
private class DirectiveWalker : RazorIRNodeWalker
|
||||||
{
|
{
|
||||||
private ClassDeclarationIRNode _classNode;
|
public ClassDeclarationIRNode ClassNode { get; private set; }
|
||||||
private readonly bool _designTime;
|
|
||||||
|
|
||||||
public DirectiveWalker(bool designTime)
|
public IList<DirectiveIRNode> FunctionsDirectiveNodes { get; } = new List<DirectiveIRNode>();
|
||||||
{
|
|
||||||
_designTime = designTime;
|
public IList<DirectiveIRNode> InheritsDirectiveNodes { get; } = new List<DirectiveIRNode>();
|
||||||
}
|
|
||||||
|
public IList<DirectiveIRNode> SectionDirectiveNodes { get; } = new List<DirectiveIRNode>();
|
||||||
|
|
||||||
public override void VisitClass(ClassDeclarationIRNode node)
|
public override void VisitClass(ClassDeclarationIRNode node)
|
||||||
{
|
{
|
||||||
if (_classNode == null)
|
if (ClassNode == null)
|
||||||
{
|
{
|
||||||
_classNode = node;
|
ClassNode = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
VisitDefault(node);
|
VisitDefault(node);
|
||||||
|
|
@ -47,47 +94,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
if (string.Equals(node.Name, CSharpCodeParser.FunctionsDirectiveDescriptor.Name, StringComparison.Ordinal))
|
if (string.Equals(node.Name, CSharpCodeParser.FunctionsDirectiveDescriptor.Name, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
foreach (var child in node.Children.Except(node.Tokens))
|
FunctionsDirectiveNodes.Add(node);
|
||||||
{
|
|
||||||
child.Parent = _classNode;
|
|
||||||
_classNode.Children.Add(child);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (string.Equals(node.Name, CSharpCodeParser.InheritsDirectiveDescriptor.Name, StringComparison.Ordinal))
|
else if (string.Equals(node.Name, CSharpCodeParser.InheritsDirectiveDescriptor.Name, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
var token = node.Tokens.FirstOrDefault();
|
InheritsDirectiveNodes.Add(node);
|
||||||
|
|
||||||
if (token != null)
|
|
||||||
{
|
|
||||||
_classNode.BaseType = token.Content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (string.Equals(node.Name, CSharpCodeParser.SectionDirectiveDescriptor.Name, StringComparison.Ordinal))
|
else if (string.Equals(node.Name, CSharpCodeParser.SectionDirectiveDescriptor.Name, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
var sectionIndex = node.Parent.Children.IndexOf(node);
|
SectionDirectiveNodes.Add(node);
|
||||||
|
|
||||||
var defineSectionEndStatement = new CSharpStatementIRNode()
|
|
||||||
{
|
|
||||||
Content = "});",
|
|
||||||
};
|
|
||||||
node.Parent.Children.Insert(sectionIndex, defineSectionEndStatement);
|
|
||||||
|
|
||||||
foreach (var child in node.Children.Except(node.Tokens).Reverse())
|
|
||||||
{
|
|
||||||
node.Parent.Children.Insert(sectionIndex, child);
|
|
||||||
}
|
|
||||||
|
|
||||||
var lambdaContent = _designTime ? "__razor_section_writer" : string.Empty;
|
|
||||||
var sectionName = node.Tokens.FirstOrDefault()?.Content;
|
|
||||||
var defineSectionStartStatement = new CSharpStatementIRNode()
|
|
||||||
{
|
|
||||||
Content = /* ORIGINAL: DefineSectionMethodName */ $"DefineSection(\"{sectionName}\", async ({lambdaContent}) => {{",
|
|
||||||
};
|
|
||||||
|
|
||||||
node.Parent.Children.Insert(sectionIndex, defineSectionStartStatement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node.Parent.Children.Remove(node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
internal class DefaultDocumentClassifierPass : DocumentClassifierPassBase
|
internal class DefaultDocumentClassifierPass : DocumentClassifierPassBase
|
||||||
{
|
{
|
||||||
public override int Order => RazorIRPass.DefaultDocumentClassifierOrder;
|
public override int Order => RazorIRPass.DefaultFeatureOrder;
|
||||||
|
|
||||||
protected override string DocumentKind => "default";
|
protected override string DocumentKind => "default";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
public class DefaultInstrumentationPass : RazorIRPassBase
|
public class DefaultInstrumentationPass : RazorIRPassBase, IRazorIROptimizationPass
|
||||||
{
|
{
|
||||||
public override int Order => RazorIRPass.DefaultLoweringOrder;
|
public override int Order => RazorIRPass.DefaultFeatureOrder;
|
||||||
|
|
||||||
public override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
public override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
{
|
{
|
||||||
var walker = new Visitor();
|
var walker = new Visitor();
|
||||||
walker.VisitDocument(irDocument);
|
walker.VisitDocument(irDocument);
|
||||||
|
|
@ -22,8 +22,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
AddInstrumentation(node);
|
AddInstrumentation(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
return irDocument;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddInstrumentation(InstrumentationItem item)
|
private static void AddInstrumentation(InstrumentationItem item)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal class DefaultRazorDirectiveClassifierPhase : RazorEnginePhaseBase, IRazorDirectiveClassifierPhase
|
||||||
|
{
|
||||||
|
public IRazorDirectiveClassifierPass[] Passes { get; private set; }
|
||||||
|
|
||||||
|
protected override void OnIntialized()
|
||||||
|
{
|
||||||
|
Passes = Engine.Features.OfType<IRazorDirectiveClassifierPass>().OrderBy(p => p.Order).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||||
|
{
|
||||||
|
var irDocument = codeDocument.GetIRDocument();
|
||||||
|
ThrowForMissingDependency(irDocument);
|
||||||
|
|
||||||
|
foreach (var pass in Passes)
|
||||||
|
{
|
||||||
|
pass.Execute(codeDocument, irDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
codeDocument.SetIRDocument(irDocument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal class DefaultRazorDocumentClassifierPhase : RazorEnginePhaseBase, IRazorDocumentClassifierPhase
|
||||||
|
{
|
||||||
|
public IRazorDocumentClassifierPass[] Passes { get; private set; }
|
||||||
|
|
||||||
|
protected override void OnIntialized()
|
||||||
|
{
|
||||||
|
Passes = Engine.Features.OfType<IRazorDocumentClassifierPass>().OrderBy(p => p.Order).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||||
|
{
|
||||||
|
var irDocument = codeDocument.GetIRDocument();
|
||||||
|
ThrowForMissingDependency(irDocument);
|
||||||
|
|
||||||
|
foreach (var pass in Passes)
|
||||||
|
{
|
||||||
|
pass.Execute(codeDocument, irDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
codeDocument.SetIRDocument(irDocument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,13 +5,13 @@ using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
internal class DefaultRazorIRPhase : RazorEnginePhaseBase, IRazorIRPhase
|
internal class DefaultRazorIROptimizationPhase : RazorEnginePhaseBase, IRazorIROptimizationPhase
|
||||||
{
|
{
|
||||||
public IRazorIRPass[] Passes { get; private set; }
|
public IRazorIROptimizationPass[] Passes { get; private set; }
|
||||||
|
|
||||||
protected override void OnIntialized()
|
protected override void OnIntialized()
|
||||||
{
|
{
|
||||||
Passes = Engine.Features.OfType<IRazorIRPass>().OrderBy(p => p.Order).ToArray();
|
Passes = Engine.Features.OfType<IRazorIROptimizationPass>().OrderBy(p => p.Order).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
foreach (var pass in Passes)
|
foreach (var pass in Passes)
|
||||||
{
|
{
|
||||||
irDocument = pass.Execute(codeDocument, irDocument);
|
pass.Execute(codeDocument, irDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
codeDocument.SetIRDocument(irDocument);
|
codeDocument.SetIRDocument(irDocument);
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal class DirectiveRemovalIROptimizationPass : RazorIRPassBase, IRazorIROptimizationPass
|
||||||
|
{
|
||||||
|
public override int Order => RazorIRPass.DefaultFeatureOrder + 50;
|
||||||
|
|
||||||
|
public override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
|
{
|
||||||
|
var visitor = new Visitor();
|
||||||
|
visitor.VisitDocument(irDocument);
|
||||||
|
|
||||||
|
foreach (var node in visitor.DirectiveNodes)
|
||||||
|
{
|
||||||
|
node.Parent.Children.Remove(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Visitor : RazorIRNodeWalker
|
||||||
|
{
|
||||||
|
public IList<DirectiveIRNode> DirectiveNodes { get; } = new List<DirectiveIRNode>();
|
||||||
|
|
||||||
|
public override void VisitDirective(DirectiveIRNode node)
|
||||||
|
{
|
||||||
|
DirectiveNodes.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,30 +7,26 @@ using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
public abstract class DocumentClassifierPassBase : RazorIRPassBase
|
public abstract class DocumentClassifierPassBase : RazorIRPassBase, IRazorDocumentClassifierPass
|
||||||
{
|
{
|
||||||
protected abstract string DocumentKind { get; }
|
protected abstract string DocumentKind { get; }
|
||||||
|
|
||||||
public override int Order => RazorIRPass.DocumentClassifierOrder;
|
public sealed override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
|
|
||||||
public sealed override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
|
||||||
{
|
{
|
||||||
if (irDocument.DocumentKind != null)
|
if (irDocument.DocumentKind != null)
|
||||||
{
|
{
|
||||||
return irDocument;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsMatch(codeDocument, irDocument))
|
if (!IsMatch(codeDocument, irDocument))
|
||||||
{
|
{
|
||||||
return irDocument;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
irDocument.DocumentKind = DocumentKind;
|
irDocument.DocumentKind = DocumentKind;
|
||||||
irDocument.Target = CreateTarget(codeDocument, irDocument.Options);
|
irDocument.Target = CreateTarget(codeDocument, irDocument.Options);
|
||||||
|
|
||||||
Rewrite(codeDocument, irDocument);
|
Rewrite(codeDocument, irDocument);
|
||||||
|
|
||||||
return irDocument;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Rewrite(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
private void Rewrite(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,14 @@
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Generates C# code using the IR document.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// After IR processing, the <see cref="IRazorCSharpLoweringPhase"/> transforms the IR document into generated C# code.
|
||||||
|
/// At this time any directives or IR constructs that cannot be understood by code generation will result
|
||||||
|
/// in an error.
|
||||||
|
/// </remarks>
|
||||||
public interface IRazorCSharpLoweringPhase : IRazorEnginePhase
|
public interface IRazorCSharpLoweringPhase : IRazorEnginePhase
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public interface IRazorDirectiveClassifierPass : IRazorEngineFeature
|
||||||
|
{
|
||||||
|
int Order { get; }
|
||||||
|
|
||||||
|
void Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Understands directive IR nodes and performs the necessary modifications to the IR document.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// The second phase of IR processing is directive classification. IR passes in this phase should interpret
|
||||||
|
/// directives and processing them accordingly by transforming IR nodes or adding diagnostics to the IR. At
|
||||||
|
/// this time the document kind has been identified, so any directive that can't be applied should trigger
|
||||||
|
/// errors. If implementing a document kind that diverges from the standard structure of Razor documents
|
||||||
|
/// it may be necessary to reimplement processing of default directives.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="IRazorDirectiveClassifierPass"/> objects are executed according to an ascending ordering of the
|
||||||
|
/// <see cref="IRazorDirectiveClassifierPass.Order"/> property. The default configuration of <see cref="RazorEngine"/>
|
||||||
|
/// prescribes a logical ordering of specific phases of IR processing.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
public interface IRazorDirectiveClassifierPhase : IRazorEnginePhase
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public interface IRazorDocumentClassifierPass : IRazorEngineFeature
|
||||||
|
{
|
||||||
|
int Order { get; }
|
||||||
|
|
||||||
|
void Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Modifies the IR document to a desired structure.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// The first phase of IR procesing is document classification. IR passes in this phase should classify the
|
||||||
|
/// document according to any relevant criteria (project configuration, file extension, directive) and modify
|
||||||
|
/// the IR tree to suit the desired document shape. Document classifiers should also set
|
||||||
|
/// <see cref="DocumentIRNode.DocumentKind"/> to prevent other classifiers from running. If no classifier
|
||||||
|
/// matches the document, then it will be classified as "generic" and processed according to set
|
||||||
|
/// of reasonable defaults.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="IRazorDocumentClassifierPass"/> objects are executed according to an ascending ordering of the
|
||||||
|
/// <see cref="IRazorDocumentClassifierPass.Order"/> property. The default configuration of <see cref="RazorEngine"/>
|
||||||
|
/// prescribes a logical ordering of specific phases of IR processing.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
public interface IRazorDocumentClassifierPhase : IRazorEnginePhase
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,16 @@
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Generates the IR document from <see cref="RazorSyntaxTree"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The IR document is first produced by <see cref="IRazorIRLoweringPhase"/>. At this point no IR passes have
|
||||||
|
/// been executed. The default <see cref="IRazorIRLoweringPhase"/> will perform a mechanical transformation
|
||||||
|
/// of the syntax tree to IR resulting in a mostly flat structure. It is up to later phases to give the document
|
||||||
|
/// structure and semantics according to a document kind. The default <see cref="IRazorIRLoweringPhase"/> is
|
||||||
|
/// also responsible for synthesizing IR nodes for global cross-current concerns such as checksums or global settings.
|
||||||
|
/// </remarks>
|
||||||
public interface IRazorIRLoweringPhase : IRazorEnginePhase
|
public interface IRazorIRLoweringPhase : IRazorEnginePhase
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
public interface IRazorIRPass : IRazorEngineFeature
|
public interface IRazorIROptimizationPass : IRazorEngineFeature
|
||||||
{
|
{
|
||||||
int Order { get; }
|
int Order { get; }
|
||||||
|
|
||||||
DocumentIRNode Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
|
void Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Performs necessary modifications to the IR document to optimize code generation.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// The last phase of IR processing is lowering. IR passes in this phase perform some kind of transformation
|
||||||
|
/// on the IR that optimizes the generated code. The key distinction here is that information may be discarded
|
||||||
|
/// during this phase.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="IRazorIROptimizationPass"/> objects are executed according to an ascending ordering of the
|
||||||
|
/// <see cref="IRazorIROptimizationPass.Order"/> property. The default configuration of <see cref="RazorEngine"/>
|
||||||
|
/// prescribes a logical ordering of specific phases of IR processing.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
public interface IRazorIROptimizationPhase : IRazorEnginePhase
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +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.
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
|
||||||
{
|
|
||||||
public interface IRazorIRPhase : IRazorEnginePhase
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,18 +6,17 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
internal class RazorDesignTimeIRPass : RazorIRPassBase
|
internal class RazorDesignTimeIRPass : RazorIRPassBase, IRazorDirectiveClassifierPass
|
||||||
{
|
{
|
||||||
internal const string DesignTimeVariable = "__o";
|
internal const string DesignTimeVariable = "__o";
|
||||||
|
|
||||||
public override int Order => RazorIRPass.DirectiveClassifierOrder;
|
// This needs to run before other directive classifiers.
|
||||||
|
public override int Order => -10;
|
||||||
|
|
||||||
public override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
public override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
{
|
{
|
||||||
var walker = new DesignTimeHelperWalker();
|
var walker = new DesignTimeHelperWalker();
|
||||||
walker.VisitDocument(irDocument);
|
walker.VisitDocument(irDocument);
|
||||||
|
|
||||||
return irDocument;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DesignTimeHelperWalker : RazorIRNodeWalker
|
internal class DesignTimeHelperWalker : RazorIRNodeWalker
|
||||||
|
|
@ -26,7 +25,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
public override void VisitClass(ClassDeclarationIRNode node)
|
public override void VisitClass(ClassDeclarationIRNode node)
|
||||||
{
|
{
|
||||||
|
|
||||||
var designTimeHelperDeclaration = new CSharpStatementIRNode()
|
var designTimeHelperDeclaration = new CSharpStatementIRNode()
|
||||||
{
|
{
|
||||||
Content = $"private static {typeof(object).FullName} {DesignTimeVariable} = null;",
|
Content = $"private static {typeof(object).FullName} {DesignTimeVariable} = null;",
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
builder.Phases.Add(new DefaultRazorParsingPhase());
|
builder.Phases.Add(new DefaultRazorParsingPhase());
|
||||||
builder.Phases.Add(new DefaultRazorSyntaxTreePhase());
|
builder.Phases.Add(new DefaultRazorSyntaxTreePhase());
|
||||||
builder.Phases.Add(new DefaultRazorIRLoweringPhase());
|
builder.Phases.Add(new DefaultRazorIRLoweringPhase());
|
||||||
builder.Phases.Add(new DefaultRazorIRPhase());
|
builder.Phases.Add(new DefaultRazorDocumentClassifierPhase());
|
||||||
|
builder.Phases.Add(new DefaultRazorDirectiveClassifierPhase());
|
||||||
|
builder.Phases.Add(new DefaultRazorIROptimizationPhase());
|
||||||
builder.Phases.Add(new DefaultRazorCSharpLoweringPhase());
|
builder.Phases.Add(new DefaultRazorCSharpLoweringPhase());
|
||||||
|
|
||||||
// Syntax Tree passes
|
// Syntax Tree passes
|
||||||
|
|
@ -62,6 +64,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
// IR Passes
|
// IR Passes
|
||||||
builder.Features.Add(new DefaultDocumentClassifierPass());
|
builder.Features.Add(new DefaultDocumentClassifierPass());
|
||||||
builder.Features.Add(new DefaultDirectiveIRPass());
|
builder.Features.Add(new DefaultDirectiveIRPass());
|
||||||
|
builder.Features.Add(new DirectiveRemovalIROptimizationPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void AddRuntimeDefaults(IRazorEngineBuilder builder)
|
internal static void AddRuntimeDefaults(IRazorEngineBuilder builder)
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,18 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Provides constants for ordering of <see cref="IRazorIRPass"/> objects. When implementing an
|
|
||||||
/// <see cref="IRazorIRPass"/>, choose a value for <see cref="IRazorIRPass.Order"/> according to
|
|
||||||
/// the logical task that must be performed.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// <see cref="IRazorIRPass"/> objects are executed according to an ascending ordering of the
|
|
||||||
/// <see cref="IRazorIRPass.Order"/> property. The default configuration of <see cref="RazorEngine"/>
|
|
||||||
/// prescribes a logical ordering of specific phases of IR processing.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The IR document is first produced by <see cref="IRazorIRLoweringPhase"/>. At this point no IR passes have
|
|
||||||
/// been executed. The default <see cref="IRazorIRLoweringPhase"/> will perform a mechanical transformation
|
|
||||||
/// of the syntax tree to IR resulting in a mostly flat structure. It is up to later phases to give the document
|
|
||||||
/// structure and semantics according to a document kind. The default <see cref="IRazorIRLoweringPhase"/> is
|
|
||||||
/// also responsible for synthesizing IR nodes for global cross-current concerns such as checksums or global settings.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The first phase of IR procesing is document classification. IR passes in this phase should classify the
|
|
||||||
/// document according to any relevant criteria (project configuration, file extension, directive) and modify
|
|
||||||
/// the IR tree to suit the desired document shape. Document classifiers should also set
|
|
||||||
/// <see cref="DocumentIRNode.DocumentKind"/> to prevent other classifiers from running. If no classifier
|
|
||||||
/// matches the document, then it will be classified as "generic" and processed according to set
|
|
||||||
/// of reasonable defaults.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The second phase of IR processing is directive classification. IR passes in this phase should interpret
|
|
||||||
/// directives and processing them accordingly by transforming IR nodes or adding diagnostics to the IR. At
|
|
||||||
/// this time the document kind has been identified, so any directive that can't be applied should trigger
|
|
||||||
/// errors. If implementing a document kind that diverges from the standard structure of Razor documents
|
|
||||||
/// it may be necessary to reimplement processing of default directives.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// The last phase of IR processing is lowering. IR passes in this phase perform some kind of transformation
|
|
||||||
/// on the IR that optimizes the generated code. The key distinction here is that information may be discarded
|
|
||||||
/// during this phase.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Finally, the <see cref="IRazorCSharpLoweringPhase"/> transforms the IR document into generated C# code.
|
|
||||||
/// At this time any directives or IR constructs that cannot be understood by code generation will result
|
|
||||||
/// in an error.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public static class RazorIRPass
|
public static class RazorIRPass
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An <see cref="IRazorIRPass"/> that implements a document classifier should use this value as its
|
/// The default implementation of the <see cref="IRazorEngineFeature"/>s that run in a
|
||||||
/// <see cref="IRazorIRPass.Order"/>.
|
/// <see cref="IRazorEnginePhase"/> will use this value for its Order property.
|
||||||
/// </summary>
|
|
||||||
public static readonly int DocumentClassifierOrder = 1100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <see cref="IRazorIRPass.Order"/> value used by the default document classifier.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly int DefaultDocumentClassifierOrder = 1900;
|
/// <remarks>
|
||||||
|
/// This value is chosen in such a way that the default implementation runs after the other
|
||||||
/// <summary>
|
/// custom <see cref="IRazorEngineFeature"/> implementations for a particular <see cref="IRazorEnginePhase"/>.
|
||||||
/// An <see cref="IRazorIRPass"/> that implements a directive classifier should use this value as its
|
/// </remarks>
|
||||||
/// <see cref="IRazorIRPass.Order"/>.
|
public static readonly int DefaultFeatureOrder = 1000;
|
||||||
/// </summary>
|
|
||||||
public static readonly int DirectiveClassifierOrder = 2100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <see cref="IRazorIRPass.Order"/> value used by the default directive classifier.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly int DefaultDirectiveClassifierOrder = 2900;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IRazorIRPass"/> that implements a lowering phase should use this value as its
|
|
||||||
/// <see cref="IRazorIRPass.Order"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly int LoweringOrder = 4100;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <see cref="IRazorIRPass.Order"/> value used by the default lowering phase.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly int DefaultLoweringOrder = 4900;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
public abstract class RazorIRPassBase : IRazorIRPass
|
public abstract class RazorIRPassBase
|
||||||
{
|
{
|
||||||
private RazorEngine _engine;
|
private RazorEngine _engine;
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract int Order { get; }
|
public virtual int Order { get; }
|
||||||
|
|
||||||
protected void ThrowForMissingDocumentDependency<TDocumentDependency>(TDocumentDependency value)
|
protected void ThrowForMissingDocumentDependency<TDocumentDependency>(TDocumentDependency value)
|
||||||
{
|
{
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public DocumentIRNode Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
public void Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
{
|
{
|
||||||
if (codeDocument == null)
|
if (codeDocument == null)
|
||||||
{
|
{
|
||||||
|
|
@ -72,9 +72,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
throw new InvalidOperationException(Resources.FormatPhaseMustBeInitialized(nameof(Engine)));
|
throw new InvalidOperationException(Resources.FormatPhaseMustBeInitialized(nameof(Engine)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExecuteCore(codeDocument, irDocument);
|
ExecuteCore(codeDocument, irDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
|
public abstract void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,14 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
internal class RazorPreallocatedTagHelperAttributeOptimizationPass : RazorIRPassBase
|
internal class RazorPreallocatedTagHelperAttributeOptimizationPass : RazorIRPassBase, IRazorIROptimizationPass
|
||||||
{
|
{
|
||||||
public override int Order => RazorIRPass.DefaultLoweringOrder;
|
public override int Order => RazorIRPass.DefaultFeatureOrder;
|
||||||
|
|
||||||
public override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
public override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
{
|
{
|
||||||
var walker = new PreallocatedTagHelperWalker();
|
var walker = new PreallocatedTagHelperWalker();
|
||||||
walker.VisitDocument(irDocument);
|
walker.VisitDocument(irDocument);
|
||||||
|
|
||||||
return irDocument;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class PreallocatedTagHelperWalker : RazorIRNodeWalker
|
internal class PreallocatedTagHelperWalker : RazorIRNodeWalker
|
||||||
|
|
@ -9,31 +9,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
public class DefaultDirectiveIRPassTest
|
public class DefaultDirectiveIRPassTest
|
||||||
{
|
{
|
||||||
[Fact]
|
|
||||||
public void Execute_MutatesIRDocument()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var content =
|
|
||||||
@"@inherits Hello<World[]>
|
|
||||||
@functions {
|
|
||||||
var value = true;
|
|
||||||
}";
|
|
||||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
|
||||||
var originalIRDocument = Lower(codeDocument);
|
|
||||||
var defaultEngine = RazorEngine.Create();
|
|
||||||
var pass = new DefaultDirectiveIRPass()
|
|
||||||
{
|
|
||||||
Engine = defaultEngine,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var irDocument = pass.Execute(codeDocument, originalIRDocument);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Same(originalIRDocument, irDocument);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Execute_Inherits_SetsClassDeclarationBaseType()
|
public void Execute_Inherits_SetsClassDeclarationBaseType()
|
||||||
{
|
{
|
||||||
|
|
@ -41,7 +16,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var content = "@inherits Hello<World[]>";
|
var content = "@inherits Hello<World[]>";
|
||||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
var originalIRDocument = Lower(codeDocument);
|
var irDocument = Lower(codeDocument);
|
||||||
var defaultEngine = RazorEngine.Create();
|
var defaultEngine = RazorEngine.Create();
|
||||||
var pass = new DefaultDirectiveIRPass()
|
var pass = new DefaultDirectiveIRPass()
|
||||||
{
|
{
|
||||||
|
|
@ -49,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var irDocument = pass.Execute(codeDocument, originalIRDocument);
|
pass.Execute(codeDocument, irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(irDocument,
|
Children(irDocument,
|
||||||
|
|
@ -71,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var content = "@functions { var value = true; }";
|
var content = "@functions { var value = true; }";
|
||||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
var originalIRDocument = Lower(codeDocument);
|
var irDocument = Lower(codeDocument);
|
||||||
var defaultEngine = RazorEngine.Create();
|
var defaultEngine = RazorEngine.Create();
|
||||||
var pass = new DefaultDirectiveIRPass()
|
var pass = new DefaultDirectiveIRPass()
|
||||||
{
|
{
|
||||||
|
|
@ -79,7 +54,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var irDocument = pass.Execute(codeDocument, originalIRDocument);
|
pass.Execute(codeDocument, irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(irDocument,
|
Children(irDocument,
|
||||||
|
|
@ -105,7 +80,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var content = "@section Header { <p>Hello World</p> }";
|
var content = "@section Header { <p>Hello World</p> }";
|
||||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
var originalIRDocument = Lower(codeDocument);
|
var irDocument = Lower(codeDocument);
|
||||||
var defaultEngine = RazorEngine.Create();
|
var defaultEngine = RazorEngine.Create();
|
||||||
var pass = new DefaultDirectiveIRPass()
|
var pass = new DefaultDirectiveIRPass()
|
||||||
{
|
{
|
||||||
|
|
@ -113,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var irDocument = pass.Execute(codeDocument, originalIRDocument);
|
pass.Execute(codeDocument, irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(irDocument,
|
Children(irDocument,
|
||||||
|
|
@ -140,7 +115,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var designTimeEngine = RazorEngine.CreateDesignTime();
|
var designTimeEngine = RazorEngine.CreateDesignTime();
|
||||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
var originalIRDocument = Lower(codeDocument, designTimeEngine);
|
var irDocument = Lower(codeDocument, designTimeEngine);
|
||||||
var defaultEngine = RazorEngine.Create();
|
var defaultEngine = RazorEngine.Create();
|
||||||
var pass = new DefaultDirectiveIRPass()
|
var pass = new DefaultDirectiveIRPass()
|
||||||
{
|
{
|
||||||
|
|
@ -148,7 +123,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var irDocument = pass.Execute(codeDocument, originalIRDocument);
|
pass.Execute(codeDocument, irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(irDocument,
|
Children(irDocument,
|
||||||
|
|
@ -167,41 +142,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
node => CSharpStatement("});", node));
|
node => CSharpStatement("});", node));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Execute_Custom_RemovesDirectiveIRNodeFromIRDocument()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var content = "@custom Hello";
|
|
||||||
var sourceDocument = TestRazorSourceDocument.Create(content);
|
|
||||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
|
||||||
var defaultEngine = RazorEngine.Create(b =>
|
|
||||||
{
|
|
||||||
var customDirective = DirectiveDescriptorBuilder.Create("custom").AddString().Build();
|
|
||||||
b.AddDirective(customDirective);
|
|
||||||
});
|
|
||||||
var originalIRDocument = Lower(codeDocument, defaultEngine);
|
|
||||||
var pass = new DefaultDirectiveIRPass()
|
|
||||||
{
|
|
||||||
Engine = defaultEngine,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var irDocument = pass.Execute(codeDocument, originalIRDocument);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Children(irDocument,
|
|
||||||
node => Assert.IsType<ChecksumIRNode>(node),
|
|
||||||
node => Assert.IsType<NamespaceDeclarationIRNode>(node));
|
|
||||||
var @namespace = irDocument.Children[1];
|
|
||||||
Children(@namespace,
|
|
||||||
node => Assert.IsType<UsingStatementIRNode>(node),
|
|
||||||
node => Assert.IsType<UsingStatementIRNode>(node),
|
|
||||||
node => Assert.IsType<ClassDeclarationIRNode>(node));
|
|
||||||
var @class = @namespace.Children[2];
|
|
||||||
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
|
||||||
Assert.Empty(method.Children);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DocumentIRNode Lower(RazorCodeDocument codeDocument)
|
private static DocumentIRNode Lower(RazorCodeDocument codeDocument)
|
||||||
{
|
{
|
||||||
var engine = RazorEngine.Create();
|
var engine = RazorEngine.Create();
|
||||||
|
|
@ -216,7 +156,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var phase = engine.Phases[i];
|
var phase = engine.Phases[i];
|
||||||
phase.Execute(codeDocument);
|
phase.Execute(codeDocument);
|
||||||
|
|
||||||
if (phase is IRazorIRLoweringPhase)
|
if (phase is IRazorDocumentClassifierPhase)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -225,8 +165,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var irDocument = codeDocument.GetIRDocument();
|
var irDocument = codeDocument.GetIRDocument();
|
||||||
Assert.NotNull(irDocument);
|
Assert.NotNull(irDocument);
|
||||||
|
|
||||||
// These tests depend on the document->namespace->class structure.
|
|
||||||
irDocument = new DefaultDocumentClassifierPass() { Engine = engine, }.Execute(codeDocument, irDocument);
|
|
||||||
return irDocument;
|
return irDocument;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -103,7 +103,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -185,7 +185,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -252,7 +252,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -283,7 +283,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var pass = new DefaultInstrumentationPass();
|
var pass = new DefaultInstrumentationPass();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
pass.ExecuteCore(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
// 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.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public class DefaultRazorDirectiveClassifierPhaseTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void OnInitialized_OrdersPassesInAscendingOrder()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var phase = new DefaultRazorDirectiveClassifierPhase();
|
||||||
|
|
||||||
|
var first = Mock.Of<IRazorDirectiveClassifierPass>(p => p.Order == 15);
|
||||||
|
var second = Mock.Of<IRazorDirectiveClassifierPass>(p => p.Order == 17);
|
||||||
|
|
||||||
|
var engine = RazorEngine.CreateEmpty(b =>
|
||||||
|
{
|
||||||
|
b.Phases.Add(phase);
|
||||||
|
|
||||||
|
b.Features.Add(second);
|
||||||
|
b.Features.Add(first);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
phase.Passes,
|
||||||
|
p => Assert.Same(first, p),
|
||||||
|
p => Assert.Same(second, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_ThrowsForMissingDependency()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var phase = new DefaultRazorDirectiveClassifierPhase();
|
||||||
|
|
||||||
|
var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||||
|
|
||||||
|
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ExceptionAssert.Throws<InvalidOperationException>(
|
||||||
|
() => phase.Execute(codeDocument),
|
||||||
|
$"The '{nameof(DefaultRazorDirectiveClassifierPhase)}' phase requires a '{nameof(DocumentIRNode)}' " +
|
||||||
|
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_ExecutesPhasesInOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||||
|
|
||||||
|
// We're going to set up mocks to simulate a sequence of passes. We don't care about
|
||||||
|
// what's in the nodes, we're just going to look at the identity via strict mocks.
|
||||||
|
var originalNode = new DocumentIRNode();
|
||||||
|
var firstPassNode = new DocumentIRNode();
|
||||||
|
var secondPassNode = new DocumentIRNode();
|
||||||
|
codeDocument.SetIRDocument(originalNode);
|
||||||
|
|
||||||
|
var firstPass = new Mock<IRazorDirectiveClassifierPass>(MockBehavior.Strict);
|
||||||
|
firstPass.SetupGet(m => m.Order).Returns(0);
|
||||||
|
firstPass.SetupProperty(m => m.Engine);
|
||||||
|
firstPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||||
|
{
|
||||||
|
originalNode.Children.Add(firstPassNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
var secondPass = new Mock<IRazorDirectiveClassifierPass>(MockBehavior.Strict);
|
||||||
|
secondPass.SetupGet(m => m.Order).Returns(1);
|
||||||
|
secondPass.SetupProperty(m => m.Engine);
|
||||||
|
secondPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||||
|
{
|
||||||
|
// Works only when the first pass has run before this.
|
||||||
|
originalNode.Children[0].Children.Add(secondPassNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
var phase = new DefaultRazorDirectiveClassifierPhase();
|
||||||
|
|
||||||
|
var engine = RazorEngine.CreateEmpty(b =>
|
||||||
|
{
|
||||||
|
b.Phases.Add(phase);
|
||||||
|
|
||||||
|
b.Features.Add(firstPass.Object);
|
||||||
|
b.Features.Add(secondPass.Object);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
phase.Execute(codeDocument);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(secondPassNode, codeDocument.GetIRDocument().Children[0].Children[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
// 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.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public class DefaultRazorDocumentClassifierPhaseTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void OnInitialized_OrdersPassesInAscendingOrder()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var phase = new DefaultRazorDocumentClassifierPhase();
|
||||||
|
|
||||||
|
var first = Mock.Of<IRazorDocumentClassifierPass>(p => p.Order == 15);
|
||||||
|
var second = Mock.Of<IRazorDocumentClassifierPass>(p => p.Order == 17);
|
||||||
|
|
||||||
|
var engine = RazorEngine.CreateEmpty(b =>
|
||||||
|
{
|
||||||
|
b.Phases.Add(phase);
|
||||||
|
|
||||||
|
b.Features.Add(second);
|
||||||
|
b.Features.Add(first);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
phase.Passes,
|
||||||
|
p => Assert.Same(first, p),
|
||||||
|
p => Assert.Same(second, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_ThrowsForMissingDependency()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var phase = new DefaultRazorDocumentClassifierPhase();
|
||||||
|
|
||||||
|
var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||||
|
|
||||||
|
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ExceptionAssert.Throws<InvalidOperationException>(
|
||||||
|
() => phase.Execute(codeDocument),
|
||||||
|
$"The '{nameof(DefaultRazorDocumentClassifierPhase)}' phase requires a '{nameof(DocumentIRNode)}' " +
|
||||||
|
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_ExecutesPhasesInOrder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||||
|
|
||||||
|
// We're going to set up mocks to simulate a sequence of passes. We don't care about
|
||||||
|
// what's in the nodes, we're just going to look at the identity via strict mocks.
|
||||||
|
var originalNode = new DocumentIRNode();
|
||||||
|
var firstPassNode = new DocumentIRNode();
|
||||||
|
var secondPassNode = new DocumentIRNode();
|
||||||
|
codeDocument.SetIRDocument(originalNode);
|
||||||
|
|
||||||
|
var firstPass = new Mock<IRazorDocumentClassifierPass>(MockBehavior.Strict);
|
||||||
|
firstPass.SetupGet(m => m.Order).Returns(0);
|
||||||
|
firstPass.SetupProperty(m => m.Engine);
|
||||||
|
firstPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||||
|
{
|
||||||
|
originalNode.Children.Add(firstPassNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
var secondPass = new Mock<IRazorDocumentClassifierPass>(MockBehavior.Strict);
|
||||||
|
secondPass.SetupGet(m => m.Order).Returns(1);
|
||||||
|
secondPass.SetupProperty(m => m.Engine);
|
||||||
|
secondPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||||
|
{
|
||||||
|
// Works only when the first pass has run before this.
|
||||||
|
originalNode.Children[0].Children.Add(secondPassNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
var phase = new DefaultRazorDocumentClassifierPhase();
|
||||||
|
|
||||||
|
var engine = RazorEngine.CreateEmpty(b =>
|
||||||
|
{
|
||||||
|
b.Phases.Add(phase);
|
||||||
|
|
||||||
|
b.Features.Add(firstPass.Object);
|
||||||
|
b.Features.Add(secondPass.Object);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
phase.Execute(codeDocument);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(secondPassNode, codeDocument.GetIRDocument().Children[0].Children[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,16 +9,16 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
public class DefaultRazorIRPhaseTest
|
public class DefaultRazorIROptimizationPhaseTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnInitialized_OrdersPassesInAscendingOrder()
|
public void OnInitialized_OrdersPassesInAscendingOrder()
|
||||||
{
|
{
|
||||||
// Arrange & Act
|
// Arrange & Act
|
||||||
var phase = new DefaultRazorIRPhase();
|
var phase = new DefaultRazorIROptimizationPhase();
|
||||||
|
|
||||||
var first = Mock.Of<IRazorIRPass>(p => p.Order == 15);
|
var first = Mock.Of<IRazorIROptimizationPass>(p => p.Order == 15);
|
||||||
var second = Mock.Of<IRazorIRPass>(p => p.Order == 17);
|
var second = Mock.Of<IRazorIROptimizationPass>(p => p.Order == 17);
|
||||||
|
|
||||||
var engine = RazorEngine.CreateEmpty(b =>
|
var engine = RazorEngine.CreateEmpty(b =>
|
||||||
{
|
{
|
||||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
public void Execute_ThrowsForMissingDependency()
|
public void Execute_ThrowsForMissingDependency()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var phase = new DefaultRazorIRPhase();
|
var phase = new DefaultRazorIROptimizationPhase();
|
||||||
|
|
||||||
var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase));
|
var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||||
|
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
ExceptionAssert.Throws<InvalidOperationException>(
|
ExceptionAssert.Throws<InvalidOperationException>(
|
||||||
() => phase.Execute(codeDocument),
|
() => phase.Execute(codeDocument),
|
||||||
$"The '{nameof(DefaultRazorIRPhase)}' phase requires a '{nameof(DocumentIRNode)}' " +
|
$"The '{nameof(DefaultRazorIROptimizationPhase)}' phase requires a '{nameof(DocumentIRNode)}' " +
|
||||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,17 +65,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var secondPassNode = new DocumentIRNode();
|
var secondPassNode = new DocumentIRNode();
|
||||||
codeDocument.SetIRDocument(originalNode);
|
codeDocument.SetIRDocument(originalNode);
|
||||||
|
|
||||||
var firstPass = new Mock<IRazorIRPass>(MockBehavior.Strict);
|
var firstPass = new Mock<IRazorIROptimizationPass>(MockBehavior.Strict);
|
||||||
firstPass.SetupGet(m => m.Order).Returns(0);
|
firstPass.SetupGet(m => m.Order).Returns(0);
|
||||||
firstPass.SetupProperty(m => m.Engine);
|
firstPass.SetupProperty(m => m.Engine);
|
||||||
firstPass.Setup(m => m.Execute(codeDocument, originalNode)).Returns(firstPassNode);
|
firstPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||||
|
{
|
||||||
|
originalNode.Children.Add(firstPassNode);
|
||||||
|
});
|
||||||
|
|
||||||
var secondPass = new Mock<IRazorIRPass>(MockBehavior.Strict);
|
var secondPass = new Mock<IRazorIROptimizationPass>(MockBehavior.Strict);
|
||||||
secondPass.SetupGet(m => m.Order).Returns(1);
|
secondPass.SetupGet(m => m.Order).Returns(1);
|
||||||
secondPass.SetupProperty(m => m.Engine);
|
secondPass.SetupProperty(m => m.Engine);
|
||||||
secondPass.Setup(m => m.Execute(codeDocument, firstPassNode)).Returns(secondPassNode);
|
secondPass.Setup(m => m.Execute(codeDocument, originalNode)).Callback(() =>
|
||||||
|
{
|
||||||
|
// Works only when the first pass has run before this.
|
||||||
|
originalNode.Children[0].Children.Add(secondPassNode);
|
||||||
|
});
|
||||||
|
|
||||||
var phase = new DefaultRazorIRPhase();
|
var phase = new DefaultRazorIROptimizationPhase();
|
||||||
|
|
||||||
var engine = RazorEngine.CreateEmpty(b =>
|
var engine = RazorEngine.CreateEmpty(b =>
|
||||||
{
|
{
|
||||||
|
|
@ -89,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
phase.Execute(codeDocument);
|
phase.Execute(codeDocument);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(secondPassNode, codeDocument.GetIRDocument());
|
Assert.Same(secondPassNode, codeDocument.GetIRDocument().Children[0].Children[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
// 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.AspNetCore.Razor.Evolution.Intermediate;
|
||||||
|
using Xunit;
|
||||||
|
using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public class DirectiveRemovalIROptimizationPassTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Execute_Custom_RemovesDirectiveIRNodeFromIRDocument()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var content = "@custom Hello";
|
||||||
|
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||||
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
|
var defaultEngine = RazorEngine.Create(b =>
|
||||||
|
{
|
||||||
|
var customDirective = DirectiveDescriptorBuilder.Create("custom").AddString().Build();
|
||||||
|
b.AddDirective(customDirective);
|
||||||
|
});
|
||||||
|
var irDocument = Lower(codeDocument, defaultEngine);
|
||||||
|
var pass = new DirectiveRemovalIROptimizationPass()
|
||||||
|
{
|
||||||
|
Engine = defaultEngine,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
pass.Execute(codeDocument, irDocument);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Children(irDocument,
|
||||||
|
node => Assert.IsType<ChecksumIRNode>(node),
|
||||||
|
node => Assert.IsType<NamespaceDeclarationIRNode>(node));
|
||||||
|
var @namespace = irDocument.Children[1];
|
||||||
|
Children(@namespace,
|
||||||
|
node => Assert.IsType<UsingStatementIRNode>(node),
|
||||||
|
node => Assert.IsType<UsingStatementIRNode>(node),
|
||||||
|
node => Assert.IsType<ClassDeclarationIRNode>(node));
|
||||||
|
var @class = @namespace.Children[2];
|
||||||
|
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||||
|
Assert.Empty(method.Children);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_MultipleCustomDirectives_RemovesDirectiveIRNodesFromIRDocument()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var content = "@custom Hello" + Environment.NewLine + "@custom World";
|
||||||
|
var sourceDocument = TestRazorSourceDocument.Create(content);
|
||||||
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
|
var defaultEngine = RazorEngine.Create(b =>
|
||||||
|
{
|
||||||
|
var customDirective = DirectiveDescriptorBuilder.Create("custom").AddString().Build();
|
||||||
|
b.AddDirective(customDirective);
|
||||||
|
});
|
||||||
|
var irDocument = Lower(codeDocument, defaultEngine);
|
||||||
|
var pass = new DirectiveRemovalIROptimizationPass()
|
||||||
|
{
|
||||||
|
Engine = defaultEngine,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
pass.Execute(codeDocument, irDocument);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Children(irDocument,
|
||||||
|
node => Assert.IsType<ChecksumIRNode>(node),
|
||||||
|
node => Assert.IsType<NamespaceDeclarationIRNode>(node));
|
||||||
|
var @namespace = irDocument.Children[1];
|
||||||
|
Children(@namespace,
|
||||||
|
node => Assert.IsType<UsingStatementIRNode>(node),
|
||||||
|
node => Assert.IsType<UsingStatementIRNode>(node),
|
||||||
|
node => Assert.IsType<ClassDeclarationIRNode>(node));
|
||||||
|
var @class = @namespace.Children[2];
|
||||||
|
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||||
|
Assert.Empty(method.Children);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DocumentIRNode Lower(RazorCodeDocument codeDocument, RazorEngine engine)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < engine.Phases.Count; i++)
|
||||||
|
{
|
||||||
|
var phase = engine.Phases[i];
|
||||||
|
phase.Execute(codeDocument);
|
||||||
|
|
||||||
|
if (phase is IRazorDirectiveClassifierPhase)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var irDocument = codeDocument.GetIRDocument();
|
||||||
|
Assert.NotNull(irDocument);
|
||||||
|
|
||||||
|
return irDocument;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -224,6 +224,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
private class TestDocumentClassifierPass : DocumentClassifierPassBase
|
private class TestDocumentClassifierPass : DocumentClassifierPassBase
|
||||||
{
|
{
|
||||||
|
public override int Order => RazorIRPass.DefaultFeatureOrder;
|
||||||
|
|
||||||
public bool ShouldMatch { get; set; } = true;
|
public bool ShouldMatch { get; set; } = true;
|
||||||
|
|
||||||
public string Namespace { get; set; }
|
public string Namespace { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1530,16 +1530,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
AssertDesignTimeDocumentMatchBaseline(document);
|
AssertDesignTimeDocumentMatchBaseline(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ApiSetsIRTestAdapter : RazorIRPassBase
|
private class ApiSetsIRTestAdapter : RazorIRPassBase, IRazorIROptimizationPass
|
||||||
{
|
{
|
||||||
public override int Order => RazorIRPass.LoweringOrder;
|
public override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||||
|
|
||||||
public override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
|
||||||
{
|
{
|
||||||
var walker = new ApiSetsIRWalker();
|
var walker = new ApiSetsIRWalker();
|
||||||
walker.Visit(irDocument);
|
walker.Visit(irDocument);
|
||||||
|
|
||||||
return irDocument;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ApiSetsIRWalker : RazorIRNodeWalker
|
private class ApiSetsIRWalker : RazorIRNodeWalker
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
||||||
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
|
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
|
||||||
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
||||||
|
feature => Assert.IsType<DirectiveRemovalIROptimizationPass>(feature),
|
||||||
feature => Assert.IsType<RazorPreallocatedTagHelperAttributeOptimizationPass>(feature));
|
feature => Assert.IsType<RazorPreallocatedTagHelperAttributeOptimizationPass>(feature));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,7 +152,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
phase => Assert.IsType<DefaultRazorParsingPhase>(phase),
|
phase => Assert.IsType<DefaultRazorParsingPhase>(phase),
|
||||||
phase => Assert.IsType<DefaultRazorSyntaxTreePhase>(phase),
|
phase => Assert.IsType<DefaultRazorSyntaxTreePhase>(phase),
|
||||||
phase => Assert.IsType<DefaultRazorIRLoweringPhase>(phase),
|
phase => Assert.IsType<DefaultRazorIRLoweringPhase>(phase),
|
||||||
phase => Assert.IsType<DefaultRazorIRPhase>(phase),
|
phase => Assert.IsType<DefaultRazorDocumentClassifierPhase>(phase),
|
||||||
|
phase => Assert.IsType<DefaultRazorDirectiveClassifierPhase>(phase),
|
||||||
|
phase => Assert.IsType<DefaultRazorIROptimizationPhase>(phase),
|
||||||
phase => Assert.IsType<DefaultRazorCSharpLoweringPhase>(phase));
|
phase => Assert.IsType<DefaultRazorCSharpLoweringPhase>(phase));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,6 +167,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
||||||
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
|
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
|
||||||
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
||||||
|
feature => Assert.IsType<DirectiveRemovalIROptimizationPass>(feature),
|
||||||
feature => Assert.IsType<RazorEngine.ConfigureDesignTimeOptions>(feature),
|
feature => Assert.IsType<RazorEngine.ConfigureDesignTimeOptions>(feature),
|
||||||
feature => Assert.IsType<RazorDesignTimeIRPass>(feature));
|
feature => Assert.IsType<RazorDesignTimeIRPass>(feature));
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +179,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
phase => Assert.IsType<DefaultRazorParsingPhase>(phase),
|
phase => Assert.IsType<DefaultRazorParsingPhase>(phase),
|
||||||
phase => Assert.IsType<DefaultRazorSyntaxTreePhase>(phase),
|
phase => Assert.IsType<DefaultRazorSyntaxTreePhase>(phase),
|
||||||
phase => Assert.IsType<DefaultRazorIRLoweringPhase>(phase),
|
phase => Assert.IsType<DefaultRazorIRLoweringPhase>(phase),
|
||||||
phase => Assert.IsType<DefaultRazorIRPhase>(phase),
|
phase => Assert.IsType<DefaultRazorDocumentClassifierPhase>(phase),
|
||||||
|
phase => Assert.IsType<DefaultRazorDirectiveClassifierPhase>(phase),
|
||||||
|
phase => Assert.IsType<DefaultRazorIROptimizationPhase>(phase),
|
||||||
phase => Assert.IsType<DefaultRazorCSharpLoweringPhase>(phase));
|
phase => Assert.IsType<DefaultRazorCSharpLoweringPhase>(phase));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue