Add annotations

Fixes #990. This should make a lot of IR manipulations very simple.
This commit is contained in:
Ryan Nowak 2017-05-10 12:09:14 -07:00
parent 51fb1b787a
commit eb3c47b6ca
34 changed files with 402 additions and 18 deletions

View File

@ -48,24 +48,14 @@ namespace Microsoft.AspNetCore.Razor.Language
var children = new List<RazorIRNode>(irDocument.Children);
irDocument.Children.Clear();
var @namespace = new NamespaceDeclarationIRNode()
{
//Content = "GeneratedNamespace",
};
var @namespace = new NamespaceDeclarationIRNode();
@namespace.Annotations[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace;
var @class = new ClassDeclarationIRNode()
{
//AccessModifier = "public",
//Name = "GeneratedClass",
};
var @class = new ClassDeclarationIRNode();
@class.Annotations[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass;
var method = new RazorMethodDeclarationIRNode()
{
//AccessModifier = "public",
// Modifiers = new List<string>() { "async" },
//Name = "Execute",
//ReturnType = "Task",
};
var method = new RazorMethodDeclarationIRNode();
method.Annotations[CommonAnnotations.PrimaryMethod] = CommonAnnotations.PrimaryMethod;
var documentBuilder = RazorIRBuilder.Create(irDocument);

View File

@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class AddTagHelperHtmlAttributeIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class CSharpAttributeValueIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class CSharpExpressionIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class CSharpStatementIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class ChecksumIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children => EmptyArray;
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class ClassDeclarationIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -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.
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
internal static class CommonAnnotations
{
public static readonly object PrimaryClass = "PrimaryClass";
public static readonly object PrimaryMethod = "PrimaryMethod";
public static readonly object PrimaryNamespace = "PrimaryNamespace";
}
}

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class CreateTagHelperIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = EmptyArray;
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class DeclareTagHelperFieldsIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = EmptyArray;
public override RazorIRNode Parent { get; set; }

View File

@ -9,6 +9,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class DirectiveIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class DirectiveTokenIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = EmptyArray;
public override RazorIRNode Parent { get; set; }

View File

@ -9,6 +9,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public sealed class DocumentIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public string DocumentKind { get; set; }

View File

@ -0,0 +1,59 @@
// 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.Language.Intermediate
{
public static class DocumentIRNodeExtensions
{
public static ClassDeclarationIRNode FindPrimaryClass(this DocumentIRNode node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
return FindWithAnnotation<ClassDeclarationIRNode>(node, CommonAnnotations.PrimaryClass);
}
public static RazorMethodDeclarationIRNode FindPrimaryMethod(this DocumentIRNode node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
return FindWithAnnotation<RazorMethodDeclarationIRNode>(node, CommonAnnotations.PrimaryMethod);
}
public static NamespaceDeclarationIRNode FindPrimaryNamespace(this DocumentIRNode node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
return FindWithAnnotation<NamespaceDeclarationIRNode>(node, CommonAnnotations.PrimaryNamespace);
}
private static T FindWithAnnotation<T>(RazorIRNode node, object annotation) where T : RazorIRNode
{
if (node is T target && object.ReferenceEquals(target.Annotations[annotation], annotation))
{
return target;
}
for (var i = 0; i < node.Children.Count; i++)
{
var result = FindWithAnnotation<T>(node.Children[i], annotation);
if (result != null)
{
return result;
}
}
return null;
}
}
}

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class ExecuteTagHelpersIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -7,6 +7,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public abstract class ExtensionIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public abstract void WriteNode(RuntimeTarget target, CSharpRenderingContext context);
protected static void AcceptExtensionNode<TNode>(TNode node, RazorIRNodeVisitor visitor)

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class HtmlAttributeIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class HtmlAttributeValueIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = EmptyArray;
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public sealed class HtmlContentIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -3,12 +3,13 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.Language.Legacy;
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class InitializeTagHelperStructureIRNode : RazorIRNode
public sealed class InitializeTagHelperStructureIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class NamespaceDeclarationIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
internal static readonly RazorIRNode[] EmptyArray = new RazorIRNode[0];
public abstract ItemCollection Annotations { get; }
public abstract IList<RazorIRNode> Children { get; }
public abstract RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public sealed class RazorIRToken : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children => RazorIRNode.EmptyArray;
public string Content { get; set; }

View File

@ -8,6 +8,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class RazorMethodDeclarationIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class SetTagHelperPropertyIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class TagHelperIRNode : RazorIRNode
{
private ItemCollection _annotations;
public override ItemCollection Annotations
{
get
{
if (_annotations == null)
{
_annotations = new DefaultItemCollection();
}
return _annotations;
}
}
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -8,6 +8,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class UsingStatementIRNode : RazorIRNode
{
public override ItemCollection Annotations => ReadonlyItemCollection.Empty;
public override IList<RazorIRNode> Children { get; } = EmptyArray;
public override RazorIRNode Parent { get; set; }

View File

@ -0,0 +1,18 @@
// 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.Language
{
internal class ReadonlyItemCollection : ItemCollection
{
public static readonly ItemCollection Empty = new ReadonlyItemCollection();
public override object this[object key]
{
get => null;
set => throw new NotSupportedException();
}
}
}

View File

@ -259,6 +259,41 @@ namespace Microsoft.AspNetCore.Razor.Language
Assert.Equal("TestMethod", method.Name);
}
[Fact]
public void Execute_AddsPrimaryAnnotations()
{
// Arrange
var irDocument = new DocumentIRNode()
{
Options = RazorParserOptions.CreateDefaultOptions(),
};
var builder = RazorIRBuilder.Create(irDocument);
builder.Add(new HtmlContentIRNode());
builder.Add(new CSharpStatementIRNode());
var pass = new TestDocumentClassifierPass()
{
Engine = RazorEngine.CreateEmpty(b => { }),
Namespace = "TestNamespace",
Class = "TestClass",
Method = "TestMethod",
};
// Act
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
// Assert
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
AnnotationEquals(@namespace, CommonAnnotations.PrimaryNamespace);
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
AnnotationEquals(@class, CommonAnnotations.PrimaryClass);
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
AnnotationEquals(method, CommonAnnotations.PrimaryMethod);
}
private class TestDocumentClassifierPass : DocumentClassifierPassBase
{
public override int Order => RazorIRPass.DefaultFeatureOrder;

View File

@ -214,6 +214,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
private class BasicIRNode : RazorIRNode
{
public override ItemCollection Annotations { get; } = new DefaultItemCollection();
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -0,0 +1,64 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public class DocumentIRNodeExtensionsTest
{
[Fact]
public void FindPrimaryClass_FindsClassWithAnnotation()
{
// Arrange
var document = new DocumentIRNode();
var @class = new ClassDeclarationIRNode();
@class.Annotations[CommonAnnotations.PrimaryClass] = CommonAnnotations.PrimaryClass;
var builder = RazorIRBuilder.Create(document);
builder.Add(@class);
// Act
var result = document.FindPrimaryClass();
// Assert
Assert.Same(@class, result);
}
[Fact]
public void FindPrimaryMethod_FindsMethodWithAnnotation()
{
// Arrange
var document = new DocumentIRNode();
var method = new RazorMethodDeclarationIRNode();
method.Annotations[CommonAnnotations.PrimaryMethod] = CommonAnnotations.PrimaryMethod;
var builder = RazorIRBuilder.Create(document);
builder.Add(method);
// Act
var result = document.FindPrimaryMethod();
// Assert
Assert.Same(method, result);
}
[Fact]
public void FindPrimaryNamespace_FindsNamespaceWithAnnotation()
{
// Arrange
var document = new DocumentIRNode();
var @namespace = new NamespaceDeclarationIRNode();
@namespace.Annotations[CommonAnnotations.PrimaryNamespace] = CommonAnnotations.PrimaryNamespace;
var builder = RazorIRBuilder.Create(document);
builder.Add(@namespace);
// Act
var result = document.FindPrimaryNamespace();
// Assert
Assert.Same(@namespace, result);
}
}
}

View File

@ -61,6 +61,37 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
}
}
public static void AnnotationEquals(RazorIRNode node, object value)
{
AnnotationEquals(node, value, value);
}
public static void AnnotationEquals(RazorIRNode node, object key, object value)
{
try
{
Assert.NotNull(node);
Assert.Equal(value, node.Annotations[key]);
}
catch (XunitException e)
{
throw new IRAssertException(node, node.Children, e.Message, e);
}
}
public static void HasAnnotation(RazorIRNode node, object key)
{
try
{
Assert.NotNull(node);
Assert.NotNull(node.Annotations[key]);
}
catch (XunitException e)
{
throw new IRAssertException(node, node.Children, e.Message, e);
}
}
public static void Html(string expected, RazorIRNode node)
{
try

View File

@ -74,6 +74,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
private class BasicIRNode : RazorIRNode
{
public override ItemCollection Annotations { get; } = new DefaultItemCollection();
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
@ -88,6 +90,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
private class BasicIRNode2 : RazorIRNode
{
public override ItemCollection Annotations { get; } = new DefaultItemCollection();
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }
@ -102,6 +106,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
private class BasicIRNode3 : RazorIRNode
{
public override ItemCollection Annotations { get; } = new DefaultItemCollection();
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }

View File

@ -70,6 +70,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
public string Name { get; }
public override ItemCollection Annotations { get; } = new DefaultItemCollection();
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
public override RazorIRNode Parent { get; set; }