Implement IR lowering phase

This is a first cut of IR lowering and includes the basic node types and
some tests.
This commit is contained in:
Ryan Nowak 2016-11-01 16:05:36 -07:00 committed by N. Taylor Mullen
parent d0e1b810c4
commit d8b626c843
57 changed files with 1571 additions and 14 deletions

View File

@ -0,0 +1,215 @@
// 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;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution
{
internal class DefaultRazorIRLoweringPhase : RazorEnginePhaseBase, IRazorIRLoweringPhase
{
protected override void ExecuteCore(RazorCodeDocument codeDocument)
{
var syntaxTree = codeDocument.GetSyntaxTree();
ThrowForMissingDependency<RazorSyntaxTree>(syntaxTree);
var visitor = new Visitor();
visitor.VisitBlock(syntaxTree.Root);
var irDocument = (DocumentIRNode)visitor.Builder.Build();
codeDocument.SetIRDocument(irDocument);
}
private class Visitor : ParserVisitor
{
public Visitor()
{
Builder = RazorIRBuilder.Document();
Namespace = new NamespaceDeclarationIRNode();
NamespaceBuilder = RazorIRBuilder.Create(Namespace);
Builder.Push(Namespace);
Class = new ClassDeclarationIRNode();
ClassBuilder = RazorIRBuilder.Create(Class);
Builder.Push(Class);
Method = new MethodDeclarationIRNode();
Builder.Push(Method);
}
public RazorIRBuilder Builder { get; }
public NamespaceDeclarationIRNode Namespace { get; }
public RazorIRBuilder NamespaceBuilder { get; }
public ClassDeclarationIRNode Class { get; }
public RazorIRBuilder ClassBuilder { get; }
public MethodDeclarationIRNode Method { get; }
public override void VisitStartAttributeBlock(AttributeBlockChunkGenerator chunk, Block block)
{
Builder.Push(new HtmlAttributeIRNode()
{
Name = chunk.Name,
Prefix = chunk.Prefix,
Suffix = chunk.Prefix,
SourceLocation = block.Start,
});
}
public override void VisitEndAttributeBlock(AttributeBlockChunkGenerator chunk, Block block)
{
Builder.Pop();
}
public override void VisitStartDynamicAttributeBlock(DynamicAttributeBlockChunkGenerator chunk, Block block)
{
Builder.Push(new CSharpAttributeValueIRNode()
{
Prefix = chunk.Prefix,
SourceLocation = block.Start,
});
}
public override void VisitEndDynamicAttributeBlock(DynamicAttributeBlockChunkGenerator chunk, Block block)
{
Builder.Pop();
}
public override void VisitLiteralAttributeSpan(LiteralAttributeChunkGenerator chunk, Span span)
{
Builder.Add(new HtmlAttributeValueIRNode()
{
Prefix = chunk.Prefix,
SourceLocation = span.Start,
});
}
public override void VisitStartTemplateBlock(TemplateBlockChunkGenerator chunk, Block block)
{
Builder.Push(new TemplateIRNode());
}
public override void VisitEndTemplateBlock(TemplateBlockChunkGenerator chunk, Block block)
{
Builder.Pop();
}
// CSharp expressions are broken up into blocks and spans because Razor allows Razor comments
// inside an expression.
// Ex:
// @DateTime.@*This is a comment*@Now
//
// We need to capture this in the IR so that we can give each piece the correct source mappings
public override void VisitStartExpressionBlock(ExpressionChunkGenerator chunk, Block block)
{
Builder.Push(new CSharpExpressionIRNode()
{
SourceLocation = block.Start,
});
}
public override void VisitEndExpressionBlock(ExpressionChunkGenerator chunk, Block block)
{
Builder.Pop();
}
public override void VisitExpressionSpan(ExpressionChunkGenerator chunk, Span span)
{
Builder.Add(new CSharpTokenIRNode()
{
Content = span.Content,
SourceLocation = span.Start,
});
}
public override void VisitStartSectionBlock(SectionChunkGenerator chunk, Block block)
{
Builder.Push(new SectionIRNode()
{
Name = chunk.SectionName,
});
}
public override void VisitEndSectionBlock(SectionChunkGenerator chunk, Block block)
{
Builder.Pop();
}
public override void VisitTypeMemberSpan(TypeMemberChunkGenerator chunk, Span span)
{
ClassBuilder.Add(new CSharpStatementIRNode()
{
Content = span.Content,
SourceLocation = span.Start,
});
}
public override void VisitAddTagHelperSpan(AddTagHelperChunkGenerator chunk, Span span)
{
// Empty for now
}
public override void VisitRemoveTagHelperSpan(RemoveTagHelperChunkGenerator chunk, Span span)
{
// Empty for now
}
public override void VisitTagHelperPrefixSpan(TagHelperPrefixDirectiveChunkGenerator chunk, Span span)
{
// Empty for now
}
public override void VisitStatementSpan(StatementChunkGenerator chunk, Span span)
{
Builder.Add(new CSharpStatementIRNode()
{
Content = span.Content,
SourceLocation = span.Start,
});
}
public override void VisitSetBaseTypeSpan(SetBaseTypeChunkGenerator chunk, Span span)
{
Class.BaseType = span.Content;
}
public override void VisitMarkupSpan(MarkupChunkGenerator chunk, Span span)
{
Builder.Add(new HtmlContentIRNode()
{
Content = span.Content,
SourceLocation = span.Start,
});
}
public override void VisitImportSpan(AddImportChunkGenerator chunk, Span span)
{
// For prettiness, let's insert the usings before the class declaration.
var i = 0;
for (; i < Namespace.Children.Count; i++)
{
if (Namespace.Children[i] is ClassDeclarationIRNode)
{
break;
}
}
var @using = new UsingStatementIRNode()
{
Content = span.Content,
Parent = Namespace,
SourceLocation = span.Start,
};
Namespace.Children.Insert(i, @using);
}
}
}
}

View File

@ -0,0 +1,9 @@
// 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 IRazorIRLoweringPhase : IRazorEnginePhase
{
}
}

View File

@ -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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class BlockDirectiveIRNode : RazorIRNode
{
public string Name { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitBlockDirective(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitBlockDirective(this);
}
}
}

View File

@ -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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class CSharpAttributeValueIRNode : RazorIRNode
{
public string Prefix { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitCSharpAttributeValue(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitCSharpAttributeValue(this);
}
}
}

View File

@ -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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class CSharpExpressionIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public string Content { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitCSharpExpression(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitCSharpExpression(this);
}
}
}

View File

@ -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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class CSharpStatementIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public string Content { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitCSharpStatement(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitCSharpStatement(this);
}
}
}

View File

@ -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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class CSharpTokenIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = EmptyArray;
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public string Content { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitCSharpToken(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitCSharpToken(this);
}
}
}

View File

@ -0,0 +1,36 @@
// 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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class ClassDeclarationIRNode : RazorIRNode
{
public string AccessModifier { get; set; }
public string Name { get; set; }
public string BaseType { get; set; }
public IList<string> Interfaces { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitClass(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitClass(this);
}
}
}

View File

@ -35,6 +35,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
Pop();
}
public override RazorIRNode Build()
{
RazorIRNode node = null;
while (_depth > 0)
{
node = Pop();
}
return node;
}
public override RazorIRNode Pop()
{
if (_depth == 0)

View File

@ -0,0 +1,27 @@
// 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.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class DirectiveTokenIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitDirectiveToken(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitDirectiveToken(this);
}
}
}

View File

@ -2,22 +2,18 @@
// 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.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public sealed class RazorIRDocument : RazorIRNode
public sealed class DocumentIRNode : RazorIRNode
{
// Only allow creation of documents through the builder API because
// they can't be nested.
internal RazorIRDocument()
{
Children = new List<RazorIRNode>();
}
public override IList<RazorIRNode> Children { get; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitDocument(this);

View File

@ -0,0 +1,33 @@
// 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.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class HtmlAttributeIRNode : RazorIRNode
{
public string Name { get; set; }
public string Prefix { get; set; }
public string Suffix { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitHtmlAttribute(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitHtmlAttribute(this);
}
}
}

View File

@ -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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class HtmlAttributeValueIRNode : RazorIRNode
{
public string Prefix { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitHtmlAttributeValue(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitHtmlAttributeValue(this);
}
}
}

View File

@ -0,0 +1,40 @@
// 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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public sealed class HtmlContentIRNode : RazorIRNode
{
public string Content { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
if (visitor == null)
{
throw new ArgumentNullException(nameof(visitor));
}
visitor.VisitHtml(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
if (visitor == null)
{
throw new ArgumentNullException(nameof(visitor));
}
return visitor.VisitHtml(this);
}
}
}

View File

@ -0,0 +1,35 @@
// 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.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class MethodDeclarationIRNode : RazorIRNode
{
public string AccessModifier { get; set; }
public IList<string> Modifiers { get; set; }
public string Name { get; set; }
public string ReturnType { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitMethodDeclaration(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitMethodDeclaration(this);
}
}
}

View File

@ -0,0 +1,27 @@
// 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.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class NamespaceDeclarationIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitNamespace(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitNamespace(this);
}
}
}

View File

@ -1,14 +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.
using System;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public abstract class RazorIRBuilder
{
public static RazorIRBuilder Document()
{
return Create(new DocumentIRNode());
}
public static RazorIRBuilder Create(RazorIRNode root)
{
if (root == null)
{
throw new ArgumentNullException(nameof(root));
}
var builder = new DefaultRazorIRBuilder();
builder.Push(new RazorIRDocument());
builder.Push(root);
return builder;
}
@ -16,6 +28,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
public abstract void Add(RazorIRNode node);
public abstract RazorIRNode Build();
public abstract void Push(RazorIRNode node);
public abstract RazorIRNode Pop();

View File

@ -2,6 +2,7 @@
// 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.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
@ -13,6 +14,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
public abstract RazorIRNode Parent { get; set; }
internal abstract SourceLocation SourceLocation { get; set; }
public abstract void Accept(RazorIRNodeVisitor visitor);
public abstract TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor);

View File

@ -14,7 +14,87 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
}
public virtual void VisitDocument(RazorIRDocument node)
public virtual void VisitDirectiveToken(DirectiveTokenIRNode node)
{
VisitDefault(node);
}
public virtual void VisitTemplate(TemplateIRNode node)
{
VisitDefault(node);
}
public virtual void VisitSection(SectionIRNode node)
{
VisitDefault(node);
}
public virtual void VisitCSharpStatement(CSharpStatementIRNode node)
{
VisitDefault(node);
}
public virtual void VisitCSharpExpression(CSharpExpressionIRNode node)
{
VisitDefault(node);
}
public virtual void VisitCSharpToken(CSharpTokenIRNode node)
{
VisitDefault(node);
}
public virtual void VisitHtmlAttributeValue(HtmlAttributeValueIRNode node)
{
VisitDefault(node);
}
public virtual void VisitCSharpAttributeValue(CSharpAttributeValueIRNode node)
{
VisitDefault(node);
}
public virtual void VisitHtmlAttribute(HtmlAttributeIRNode node)
{
VisitDefault(node);
}
public virtual void VisitSingleLineDirective(SingleLineDirectiveIRNode node)
{
VisitDefault(node);
}
public virtual void VisitBlockDirective(BlockDirectiveIRNode node)
{
VisitDefault(node);
}
public virtual void VisitClass(ClassDeclarationIRNode node)
{
VisitDefault(node);
}
public virtual void VisitMethodDeclaration(MethodDeclarationIRNode node)
{
VisitDefault(node);
}
public virtual void VisitDocument(DocumentIRNode node)
{
VisitDefault(node);
}
public virtual void VisitHtml(HtmlContentIRNode node)
{
VisitDefault(node);
}
public virtual void VisitNamespace(NamespaceDeclarationIRNode node)
{
VisitDefault(node);
}
public virtual void VisitUsingStatement(UsingStatementIRNode node)
{
VisitDefault(node);
}

View File

@ -15,7 +15,86 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
return default(TResult);
}
public virtual TResult VisitDocument(RazorIRDocument node)
public virtual TResult VisitDirectiveToken(DirectiveTokenIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitTemplate(TemplateIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitSection(SectionIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitCSharpStatement(CSharpStatementIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitCSharpExpression(CSharpExpressionIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitCSharpToken(CSharpTokenIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitHtmlAttributeValue(HtmlAttributeValueIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitCSharpAttributeValue(CSharpAttributeValueIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitHtmlAttribute(HtmlAttributeIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitClass(ClassDeclarationIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitSingleLineDirective(SingleLineDirectiveIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitBlockDirective(BlockDirectiveIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitMethodDeclaration(MethodDeclarationIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitDocument(DocumentIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitHtml(HtmlContentIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitNamespace(NamespaceDeclarationIRNode node)
{
return VisitDefault(node);
}
public virtual TResult VisitUsingStatement(UsingStatementIRNode node)
{
return VisitDefault(node);
}

View File

@ -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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class SectionIRNode : RazorIRNode
{
public string Name { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitSection(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitSection(this);
}
}
}

View File

@ -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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class SingleLineDirectiveIRNode : RazorIRNode
{
public string Name { get; set; }
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitSingleLineDirective(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitSingleLineDirective(this);
}
}
}

View File

@ -0,0 +1,27 @@
// 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.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class TemplateIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
visitor.VisitTemplate(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
return visitor.VisitTemplate(this);
}
}
}

View File

@ -0,0 +1,40 @@
// 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 System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class UsingStatementIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public string Content { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
if (visitor == null)
{
throw new ArgumentNullException(nameof(visitor));
}
visitor.VisitUsingStatement(this);
}
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
{
if (visitor == null)
{
throw new ArgumentNullException(nameof(visitor));
}
return visitor.VisitUsingStatement(this);
}
}
}

View File

@ -14,6 +14,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public string Namespace { get; }
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitImportSpan(this, span);
}
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
var ns = Namespace;

View File

@ -15,6 +15,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public string LookupText { get; }
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitAddTagHelperSpan(this, span);
}
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
//context.ChunkTreeBuilder.AddAddTagHelperChunk(LookupText, target);

View File

@ -36,6 +36,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
//context.ChunkTreeBuilder.EndParentChunk();
}
public override void AcceptStart(ParserVisitor visitor, Block block)
{
visitor.VisitStartAttributeBlock(this, block);
}
public override void AcceptEnd(ParserVisitor visitor, Block block)
{
visitor.VisitEndAttributeBlock(this, block);
}
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "Attr:{0},{1:F},{2:F}", Name, Prefix, Suffix);

View File

@ -176,6 +176,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
return hashCodeCombiner.CombinedHash;
}
public override void Accept(ParserVisitor visitor)
{
visitor.VisitBlock(this);
}
private class EquivalenceComparer : IEqualityComparer<SyntaxTreeNode>
{
public static readonly EquivalenceComparer Default = new EquivalenceComparer();

View File

@ -1,6 +1,7 @@
// 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 System.Globalization;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
@ -22,6 +23,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public SourceLocation ValueStart { get; }
public override void AcceptStart(ParserVisitor visitor, Block block)
{
visitor.VisitStartDynamicAttributeBlock(this, block);
}
public override void AcceptEnd(ParserVisitor visitor, Block block)
{
visitor.VisitEndDynamicAttributeBlock(this, block);
}
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
//var chunk = context.ChunkTreeBuilder.StartParentChunk<DynamicCodeAttributeChunk>(target);

View File

@ -1,6 +1,8 @@
// 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.Evolution.Legacy
{
internal class ExpressionChunkGenerator : ISpanChunkGenerator, IParentChunkGenerator
@ -22,6 +24,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
//context.ChunkTreeBuilder.EndParentChunk();
}
public void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitExpressionSpan(this, span);
}
public void AcceptStart(ParserVisitor visitor, Block block)
{
visitor.VisitStartExpressionBlock(this, block);
}
public void AcceptEnd(ParserVisitor visitor, Block block)
{
visitor.VisitEndExpressionBlock(this, block);
}
public override string ToString()
{
return "Expr";

View File

@ -7,5 +7,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
void GenerateStartParentChunk(Block target, ChunkGeneratorContext context);
void GenerateEndParentChunk(Block target, ChunkGeneratorContext context);
void AcceptStart(ParserVisitor visitor, Block block);
void AcceptEnd(ParserVisitor visitor, Block block);
}
}

View File

@ -6,5 +6,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
internal interface ISpanChunkGenerator
{
void GenerateChunk(Span target, ChunkGeneratorContext context);
void Accept(ParserVisitor visitor, Span span);
}
}

View File

@ -28,6 +28,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public LocationTagged<SpanChunkGenerator> ValueGenerator { get; }
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitLiteralAttributeSpan(this, span);
}
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
//var chunk = context.ChunkTreeBuilder.StartParentChunk<LiteralCodeAttributeChunk>(target);

View File

@ -10,6 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
//context.ChunkTreeBuilder.AddLiteralChunk(target.Content, target);
}
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitMarkupSpan(this, span);
}
public override string ToString()
{
return "Markup";

View File

@ -9,6 +9,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public static readonly IParentChunkGenerator Null = new NullParentChunkGenerator();
public abstract void AcceptStart(ParserVisitor visitor, Block block);
public abstract void AcceptEnd(ParserVisitor visitor, Block block);
public virtual void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
}
@ -42,6 +45,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
return "None";
}
public void AcceptStart(ParserVisitor visitor, Block block)
{
}
public void AcceptEnd(ParserVisitor visitor, Block block)
{
}
}
}
}

View File

@ -0,0 +1,132 @@
// 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.Legacy
{
internal abstract class ParserVisitor
{
public virtual void VisitBlock(Block block)
{
VisitStartBlock(block);
for (var i = 0; i < block.Children.Count; i++)
{
block.Children[i].Accept(this);
}
VisitEndBlock(block);
}
public virtual void VisitStartBlock(Block block)
{
if (block.ChunkGenerator != null)
{
block.ChunkGenerator.AcceptStart(this, block);
}
}
public virtual void VisitEndBlock(Block block)
{
if (block.ChunkGenerator != null)
{
block.ChunkGenerator.AcceptEnd(this, block);
}
}
public virtual void VisitSpan(Span span)
{
if (span.ChunkGenerator != null)
{
span.ChunkGenerator.Accept(this, span);
}
}
public virtual void VisitStartDynamicAttributeBlock(DynamicAttributeBlockChunkGenerator chunk, Block block)
{
}
public virtual void VisitEndDynamicAttributeBlock(DynamicAttributeBlockChunkGenerator chunk, Block block)
{
}
public virtual void VisitStartExpressionBlock(ExpressionChunkGenerator chunk, Block block)
{
}
public virtual void VisitEndExpressionBlock(ExpressionChunkGenerator chunk, Block block)
{
}
public virtual void VisitStartAttributeBlock(AttributeBlockChunkGenerator chunk, Block block)
{
}
public virtual void VisitEndAttributeBlock(AttributeBlockChunkGenerator chunk, Block block)
{
}
public virtual void VisitExpressionSpan(ExpressionChunkGenerator chunk, Span span)
{
}
public virtual void VisitSetBaseTypeSpan(SetBaseTypeChunkGenerator chunk, Span span)
{
}
public virtual void VisitTagHelperPrefixSpan(TagHelperPrefixDirectiveChunkGenerator chunk, Span span)
{
}
public virtual void VisitTypeMemberSpan(TypeMemberChunkGenerator chunk, Span span)
{
}
public virtual void VisitMarkupSpan(MarkupChunkGenerator chunk, Span span)
{
}
public virtual void VisitRemoveTagHelperSpan(RemoveTagHelperChunkGenerator chunk, Span span)
{
}
public virtual void VisitImportSpan(AddImportChunkGenerator chunk, Span span)
{
}
public virtual void VisitAddTagHelperSpan(AddTagHelperChunkGenerator chunk, Span span)
{
}
public virtual void VisitStatementSpan(StatementChunkGenerator chunk, Span span)
{
}
public virtual void VisitLiteralAttributeSpan(LiteralAttributeChunkGenerator chunk, Span span)
{
}
public virtual void VisitEndTemplateBlock(TemplateBlockChunkGenerator chunk, Block block)
{
}
public virtual void VisitStartTemplateBlock(TemplateBlockChunkGenerator chunk, Block block)
{
}
public virtual void VisitStartSectionBlock(SectionChunkGenerator chunk, Block block)
{
}
public virtual void VisitEndSectionBlock(SectionChunkGenerator chunk, Block block)
{
}
public virtual void VisitEndCommentBlock(RazorCommentChunkGenerator chunk, Block block)
{
}
public virtual void VisitStartCommentBlock(RazorCommentChunkGenerator chunk, Block block)
{
}
}
}

View File

@ -5,5 +5,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class RazorCommentChunkGenerator : ParentChunkGenerator
{
public override void AcceptStart(ParserVisitor visitor, Block block)
{
visitor.VisitStartCommentBlock(this, block);
}
public override void AcceptEnd(ParserVisitor visitor, Block block)
{
visitor.VisitEndCommentBlock(this, block);
}
}
}

View File

@ -15,6 +15,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public string LookupText { get; }
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitRemoveTagHelperSpan(this, span);
}
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
//context.ChunkTreeBuilder.AddRemoveTagHelperChunk(LookupText, target);

View File

@ -14,6 +14,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public string SectionName { get; }
public override void AcceptStart(ParserVisitor visitor, Block block)
{
visitor.VisitStartSectionBlock(this, block);
}
public override void AcceptEnd(ParserVisitor visitor, Block block)
{
visitor.VisitEndSectionBlock(this, block);
}
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
//var chunk = context.ChunkTreeBuilder.StartParentChunk<SectionChunk>(target);

View File

@ -14,6 +14,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public string BaseType { get; }
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitSetBaseTypeSpan(this, span);
}
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
}

View File

@ -131,5 +131,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Hash code should include only immutable properties but Equals also checks the type.
return TypeHashCode;
}
public override void Accept(ParserVisitor visitor)
{
visitor.VisitSpan(this);
}
}
}

View File

@ -1,6 +1,8 @@
// 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.Evolution.Legacy
{
internal abstract class SpanChunkGenerator : ISpanChunkGenerator
@ -9,6 +11,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public static readonly ISpanChunkGenerator Null = new NullSpanChunkGenerator();
public abstract void Accept(ParserVisitor visitor, Span span);
public virtual void GenerateChunk(Span target, ChunkGeneratorContext context)
{
}
@ -26,6 +30,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private class NullSpanChunkGenerator : ISpanChunkGenerator
{
public void Accept(ParserVisitor visitor, Span span)
{
}
public void GenerateChunk(Span target, ChunkGeneratorContext context)
{
}

View File

@ -5,6 +5,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class StatementChunkGenerator : SpanChunkGenerator
{
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitStatementSpan(this, span);
}
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
//context.ChunkTreeBuilder.AddStatementChunk(target.Content, target);

View File

@ -41,5 +41,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
/// <see cref="EquivalentTo"/> comparisons.
/// </returns>
public abstract int GetEquivalenceHash();
public abstract void Accept(ParserVisitor visitor);
}
}

View File

@ -15,6 +15,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public string Prefix { get; }
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitTagHelperPrefixSpan(this, span);
}
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
//context.ChunkTreeBuilder.AddTagHelperPrefixDirectiveChunk(Prefix, target);

View File

@ -5,6 +5,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class TemplateBlockChunkGenerator : ParentChunkGenerator
{
public override void AcceptStart(ParserVisitor visitor, Block block)
{
visitor.VisitStartTemplateBlock(this, block);
}
public override void AcceptEnd(ParserVisitor visitor, Block block)
{
visitor.VisitEndTemplateBlock(this, block);
}
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
//context.ChunkTreeBuilder.StartParentChunk<TemplateChunk>(target);

View File

@ -10,6 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
//context.ChunkTreeBuilder.AddTypeMemberChunk(target.Content, target);
}
public override void Accept(ParserVisitor visitor, Span span)
{
visitor.VisitTypeMemberSpan(this, span);
}
public override string ToString()
{
return "TypeMember";

View File

@ -2,6 +2,7 @@
// 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;
namespace Microsoft.AspNetCore.Razor.Evolution
{
@ -26,5 +27,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution
document.Items[typeof(RazorSyntaxTree)] = syntaxTree;
}
public static DocumentIRNode GetIRDocument(this RazorCodeDocument document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return document.Items[typeof(DocumentIRNode)] as DocumentIRNode;
}
public static void SetIRDocument(this RazorCodeDocument document, DocumentIRNode irDocument)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
document.Items[typeof(DocumentIRNode)] = irDocument;
}
}
}

View File

@ -32,6 +32,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
builder.Phases.Add(new DefaultRazorParsingPhase());
builder.Phases.Add(new DefaultRazorSyntaxTreePhase());
builder.Phases.Add(new DefaultRazorIRLoweringPhase());
builder.Features.Add(new TagHelperBinderSyntaxTreePass());
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Microsoft.AspNetCore.Testing;
using Xunit;
@ -125,12 +126,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
Assert.Collection(parent.Children, n => Assert.Same(node, n));
}
[Fact]
public void Build_PopsMultipleLevels()
{
// Arrange
var builder = new DefaultRazorIRBuilder();
var document = new DocumentIRNode();
builder.Push(document);
var node = new BasicIRNode();
builder.Push(node);
// Act
var result = builder.Build();
// Assert
Assert.Same(document, result);
Assert.Null(builder.Current);
}
private class BasicIRNode : RazorIRNode
{
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
throw new NotImplementedException();

View File

@ -0,0 +1,118 @@
// 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 static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
using Xunit;
using System;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public class LoweringIntegrationTest
{
[Fact]
public void Lower_EmptyDocument()
{
var codeDocument = TestRazorCodeDocument.CreateEmpty();
var irDocument = Lower(codeDocument);
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
var method = SingleChild<MethodDeclarationIRNode>(@class);
var html = SingleChild<HtmlContentIRNode>(method);
Assert.Equal(string.Empty, html.Content);
}
[Fact]
public void Lower_HelloWorld()
{
var codeDocument = TestRazorCodeDocument.Create("Hello, World!");
var irDocument = Lower(codeDocument);
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
var method = SingleChild<MethodDeclarationIRNode>(@class);
var html = SingleChild<HtmlContentIRNode>(method);
Assert.Equal("Hello, World!", html.Content);
}
[Fact]
public void Lower_HtmlWithAttributes()
{
var codeDocument = TestRazorCodeDocument.Create(@"
<html>
<body>
<span data-val=""@Hello"" />
</body>
</html>");
var irDocument = Lower(codeDocument);
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
var method = SingleChild<MethodDeclarationIRNode>(@class);
Children(method,
n => Html(Environment.NewLine, n),
n => Html("<html>", n),
n => Html(Environment.NewLine + " ", n),
n => Html("<body>", n),
n => Html(Environment.NewLine + " ", n),
n => Html("<span", n),
n => Html(" data-val=\"", n),
n => CSharpExpression("Hello", n),
n => Html("\"", n),
n => Html(" />", n),
n => Html(Environment.NewLine + " ", n),
n => Html("</body>", n),
n => Html(Environment.NewLine, n),
n => Html("</html>", n));
}
[Fact]
public void Lower_WithUsing()
{
var codeDocument = TestRazorCodeDocument.Create(@"@functions { public int Foo { get; set; }}");
var irDocument = Lower(codeDocument);
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
Children(@class,
n => Assert.IsType<MethodDeclarationIRNode>(n),
n => Assert.IsType<CSharpStatementIRNode>(n));
}
[Fact]
public void Lower_WithFunctions()
{
var codeDocument = TestRazorCodeDocument.Create(@"@using System");
var irDocument = Lower(codeDocument);
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
Children(@namespace,
n => Using("using System", n),
n => Assert.IsType<ClassDeclarationIRNode>(n));
}
private DocumentIRNode Lower(RazorCodeDocument codeDocument)
{
var engine = RazorEngine.Create();
for (var i = 0; i < engine.Phases.Count; i++)
{
var phase = engine.Phases[i];
phase.Execute(codeDocument);
if (phase is IRazorIRLoweringPhase)
{
break;
}
}
var irDocument = codeDocument.GetIRDocument();
Assert.NotNull(irDocument);
return irDocument;
}
}
}

View File

@ -0,0 +1,169 @@
// 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 System.Collections.Generic;
using System.Text;
using Xunit;
using Xunit.Sdk;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
public static class RazorIRAssert
{
public static TNode SingleChild<TNode>(RazorIRNode node)
{
if (node.Children.Count == 0)
{
throw new IRAssertException(node, "The node has no children.");
}
else if (node.Children.Count > 1)
{
throw new IRAssertException(node, node.Children, "The node has multiple children");
}
var child = node.Children[0];
return Assert.IsType<TNode>(child);
}
public static void NoChildren(RazorIRNode node)
{
if (node.Children.Count > 0)
{
throw new IRAssertException(node, node.Children, "The node has children.");
}
}
public static void Children(RazorIRNode node, params Action<RazorIRNode>[] validators)
{
var i = 0;
for (; i < validators.Length; i++)
{
if (node.Children.Count == i)
{
throw new IRAssertException(node, node.Children, $"The node only has {node.Children.Count} children.");
}
try
{
validators[i].Invoke(node.Children[i]);
}
catch (XunitException e)
{
throw new IRAssertException(node, node.Children, $"Failed while validating node {node.Children[i]} at {i}.", e);
}
}
if (i < node.Children.Count)
{
throw new IRAssertException(node, node.Children, $"The node has extra child {node.Children[i]} at {i}.");
}
}
public static void Html(string expected, RazorIRNode node)
{
try
{
var html = Assert.IsType<HtmlContentIRNode>(node);
Assert.Equal(expected, html.Content);
}
catch (XunitException e)
{
throw new IRAssertException(node, node.Children, e.Message, e);
}
}
public static void Using(string expected, RazorIRNode node)
{
try
{
var @using = Assert.IsType<UsingStatementIRNode>(node);
Assert.Equal(expected, @using.Content);
}
catch (XunitException e)
{
throw new IRAssertException(node, node.Children, e.Message, e);
}
}
public static void CSharpExpression(string expected, RazorIRNode node)
{
try
{
var cSharp = Assert.IsType<CSharpExpressionIRNode>(node);
var content = new StringBuilder();
for (var i = 0; i < cSharp.Children.Count; i++)
{
content.Append(((CSharpTokenIRNode)cSharp.Children[i]).Content);
}
Assert.Equal(expected, content.ToString());
}
catch (XunitException e)
{
throw new IRAssertException(node, node.Children, e.Message, e);
}
}
private class IRAssertException : XunitException
{
public IRAssertException(RazorIRNode node, string userMessage)
: base(Format(node, null, userMessage))
{
Node = node;
}
public IRAssertException(RazorIRNode node, IEnumerable<RazorIRNode> nodes, string userMessage)
: base(Format(node, nodes, userMessage))
{
Node = node;
Nodes = nodes;
}
public IRAssertException(
RazorIRNode node,
IEnumerable<RazorIRNode> nodes,
string userMessage,
Exception innerException)
: base(Format(node, nodes, userMessage), innerException)
{
}
public RazorIRNode Node { get; }
public IEnumerable<RazorIRNode> Nodes { get; }
private static string Format(RazorIRNode node, IEnumerable<RazorIRNode> nodes, string userMessage)
{
var builder = new StringBuilder();
builder.AppendLine(userMessage);
builder.AppendLine();
if (nodes != null)
{
builder.AppendLine("Nodes:");
foreach (var n in nodes)
{
builder.AppendLine(n.ToString());
}
builder.AppendLine();
}
builder.AppendLine("Path:");
var current = node;
do
{
builder.AppendLine(current.ToString());
}
while ((current = current.Parent) != null);
return builder.ToString();
}
}
}
}

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
var builder = RazorIRBuilder.Document();
// Assert
Assert.IsType<RazorIRDocument>(builder.Current);
Assert.IsType<DocumentIRNode>(builder.Current);
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
@ -74,6 +75,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
public override RazorIRNode Parent { get; set; }
internal override SourceLocation SourceLocation { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
((DerivedIRNodeWalker)visitor).VisitBasic(this);

View File

@ -1,6 +1,7 @@
// 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;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution
@ -37,5 +38,36 @@ namespace Microsoft.AspNetCore.Razor.Evolution
// Assert
Assert.Same(expected, codeDocument.Items[typeof(RazorSyntaxTree)]);
}
[Fact]
public void GetIRDocument_ReturnsIRDocument()
{
// Arrange
var codeDocument = TestRazorCodeDocument.CreateEmpty();
var expected = RazorIRBuilder.Document().Build();
codeDocument.Items[typeof(DocumentIRNode)] = expected;
// Act
var actual = codeDocument.GetIRDocument();
// Assert
Assert.Same(expected, actual);
}
[Fact]
public void SetIRDocument_SetsIRDocument()
{
// Arrange
var codeDocument = TestRazorCodeDocument.CreateEmpty();
var expected = RazorIRBuilder.Document().Build();
// Act
codeDocument.SetIRDocument((DocumentIRNode)expected);
// Assert
Assert.Same(expected, codeDocument.Items[typeof(DocumentIRNode)]);
}
}
}

View File

@ -84,7 +84,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
Assert.Collection(
phases,
phase => Assert.IsType<DefaultRazorParsingPhase>(phase),
phase => Assert.IsType<DefaultRazorSyntaxTreePhase>(phase));
phase => Assert.IsType<DefaultRazorSyntaxTreePhase>(phase),
phase => Assert.IsType<DefaultRazorIRLoweringPhase>(phase));
}
}
}

View File

@ -11,6 +11,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return new TestRazorCodeDocument(source);
}
public static TestRazorCodeDocument Create(string content)
{
var source = TestRazorSourceDocument.Create(content);
return new TestRazorCodeDocument(source);
}
private TestRazorCodeDocument(RazorSourceDocument source)
: base(source)
{