DefaultRazorIRLoweringPhase is too agressive

This change fixes a bug where DefaultRazorIRLoweringPhase is too
aggressive in merging HTML spans. You can hit the bug by delimiting two
html spans with a metacode character like:

<foo>@{ <bar/> }</foo>

The lowering phase will combine these HTML nodes, which is invalid as they
don't have contiguous spans.

The change here is to merge spans only when they both have an invalid
location or are contiguous.
This commit is contained in:
Ryan Nowak 2017-02-15 18:03:50 -08:00
parent 7a1a6dd1d6
commit e35ee53ee5
6 changed files with 61 additions and 27 deletions

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Evolution
{
@ -176,16 +177,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution
_builder.Pop();
}
protected SourceSpan BuildSourceSpanFromNode(SyntaxTreeNode node)
protected SourceSpan? BuildSourceSpanFromNode(SyntaxTreeNode node)
{
var location = node.Start;
var sourceRange = new SourceSpan(
if (location == SourceLocation.Undefined)
{
return null;
}
var span = new SourceSpan(
node.Start.FilePath ?? Filename,
node.Start.AbsoluteIndex,
node.Start.LineIndex,
node.Start.CharacterIndex,
node.Length);
return sourceRange;
return span;
}
}
@ -227,7 +233,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
}
}
}
private class MainSourceVisitor : LoweringVisitor
{
private DeclareTagHelperFieldsIRNode _tagHelperFields;
@ -399,27 +405,43 @@ namespace Microsoft.AspNetCore.Razor.Evolution
if (currentChildren.Count > 0 && currentChildren[currentChildren.Count - 1] is HtmlContentIRNode)
{
var existingHtmlContent = (HtmlContentIRNode)currentChildren[currentChildren.Count - 1];
existingHtmlContent.Content = string.Concat(existingHtmlContent.Content, span.Content);
if (existingHtmlContent.Source != null)
var source = BuildSourceSpanFromNode(span);
if (existingHtmlContent.Source == null && source == null)
{
var contentLength = existingHtmlContent.Source.Value.Length + span.Content.Length;
Combine(existingHtmlContent, span);
return;
}
existingHtmlContent.Source = new SourceSpan(
existingHtmlContent.Source.Value.FilePath ?? Filename,
existingHtmlContent.Source.Value.AbsoluteIndex,
existingHtmlContent.Source.Value.LineIndex,
existingHtmlContent.Source.Value.CharacterIndex,
contentLength);
if (source != null &&
existingHtmlContent.Source != null &&
existingHtmlContent.Source.Value.FilePath == source.Value.FilePath &&
existingHtmlContent.Source.Value.AbsoluteIndex + existingHtmlContent.Source.Value.Length == source.Value.AbsoluteIndex)
{
Combine(existingHtmlContent, span);
return;
}
}
else
_builder.Add(new HtmlContentIRNode()
{
_builder.Add(new HtmlContentIRNode()
{
Content = span.Content,
Source = BuildSourceSpanFromNode(span),
});
Content = span.Content,
Source = BuildSourceSpanFromNode(span),
});
}
private void Combine(HtmlContentIRNode node, Span span)
{
node.Content = node.Content + span.Content;
if (node.Source != null)
{
Debug.Assert(node.Source.Value.FilePath != null);
node.Source = new SourceSpan(
node.Source.Value.FilePath,
node.Source.Value.AbsoluteIndex,
node.Source.Value.LineIndex,
node.Source.Value.CharacterIndex,
node.Content.Length);
}
}

View File

@ -27,7 +27,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#line default
#line hidden
WriteLiteral("</p>\r\n <p>Basic Asynchronous Statement Nested: <b>");
WriteLiteral("</p>\r\n <p>Basic Asynchronous Statement Nested: ");
WriteLiteral(" <b>");
#line 13 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Await.cshtml"
Write(await Foo());
@ -70,7 +71,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#line default
#line hidden
WriteLiteral("</p>\r\n <p>Advanced Asynchronous Statement Nested: <b>");
WriteLiteral("</p>\r\n <p>Advanced Asynchronous Statement Nested: ");
WriteLiteral(" <b>");
#line 24 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Await.cshtml"
Write(await Foo(boolValue: false));

View File

@ -29,13 +29,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync()
{
WriteLiteral("\r\n<div class=\"randomNonTagHelperAttribute\">\r\n <p class=\"Hello World\" ");
WriteLiteral("\r\n<");
WriteLiteral("div class=\"randomNonTagHelperAttribute\">\r\n <");
WriteLiteral("p class=\"Hello World\" ");
#line 4 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/EscapedTagHelpers.cshtml"
Write(DateTime.Now);
#line default
#line hidden
WriteLiteral(">\r\n <input type=\"text\" />\r\n <em>Not a TagHelper: </em> ");
WriteLiteral(">\r\n <");
WriteLiteral("input type=\"text\" />\r\n <");
WriteLiteral("em>Not a TagHelper: </");
WriteLiteral("em> ");
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.SelfClosing, "test", async() => {
}
);
@ -66,7 +71,9 @@ __TestNamespace_InputTagHelper2.Checked = true;
}
Write(__tagHelperExecutionContext.Output);
__tagHelperExecutionContext = __tagHelperScopeManager.End();
WriteLiteral("\r\n </p>\r\n</div>");
WriteLiteral("\r\n </");
WriteLiteral("p>\r\n</");
WriteLiteral("div>");
}
#pragma warning restore 1998
}

View File

@ -26,7 +26,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#line default
#line hidden
WriteLiteral(" Hello, World\r\n <p>Hello, World</p>\r\n");
WriteLiteral(" ");
WriteLiteral("Hello, World\r\n <p>Hello, World</p>\r\n");
WriteLiteral("\r\n");
#line 8 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/Instrumented.cshtml"
while(i <= 10) {

View File

@ -9,7 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests.TestFiles
#pragma warning disable 1998
public async System.Threading.Tasks.Task ExecuteAsync()
{
WriteLiteral("\r\n<p>This should be shown</p>\r\n\r\n");
WriteLiteral("\r\n<p>This should ");
WriteLiteral(" be shown</p>\r\n\r\n");
#line 5 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/RazorComments.cshtml"
Exception foo =

View File

@ -115,7 +115,8 @@ WriteTo(__razor_template_writer, item);
#line default
#line hidden
WriteLiteralTo(__razor_template_writer, " <ul>\r\n <li>Child Items... ?</li>\r\n </ul>\r\n </li>");
WriteLiteralTo(__razor_template_writer, " <ul>\r\n <li>Child Items... ?</li>\r\n");
WriteLiteralTo(__razor_template_writer, " </ul>\r\n </li>");
}
)));