Add Razor runtime code generation.
- Added TabSize,IsIndentingWithTabs and NamespaceImports to the RazorParser options. These are replacements for the existing RazorEngineHost abstraction. - Added RazorParserOptions consumption pattern to more than just the parsing phase. - Added a ChecksumIRNode to ensure Debugging can work. - Updated tests to to react to new Checksum and Namespace nodes in the IR tree.
This commit is contained in:
parent
2db4985c21
commit
5d4c4e1ccf
|
|
@ -0,0 +1,571 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
internal class DefaultRazorCSharpLoweringPhase : RazorEnginePhaseBase, IRazorCSharpLoweringPhase
|
||||
{
|
||||
private IRazorConfigureParserFeature[] _parserOptionsCallbacks;
|
||||
|
||||
protected override void OnIntialized()
|
||||
{
|
||||
_parserOptionsCallbacks = Engine.Features.OfType<IRazorConfigureParserFeature>().ToArray();
|
||||
}
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var irDocument = codeDocument.GetIRDocument();
|
||||
ThrowForMissingDependency(irDocument);
|
||||
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
ThrowForMissingDependency(syntaxTree);
|
||||
|
||||
var renderingContext = new CSharpRenderingContext()
|
||||
{
|
||||
Writer = new CSharpCodeWriter(),
|
||||
SourceDocument = codeDocument.Source,
|
||||
Options = syntaxTree.Options,
|
||||
};
|
||||
var visitor = new CSharpRenderer(renderingContext);
|
||||
visitor.VisitDefault(irDocument);
|
||||
var csharpDocument = new RazorCSharpDocument()
|
||||
{
|
||||
GeneratedCode = renderingContext.Writer.GenerateCode(),
|
||||
LineMappings = renderingContext.Writer.LineMappingManager.Mappings,
|
||||
};
|
||||
|
||||
codeDocument.SetCSharpDocument(csharpDocument);
|
||||
}
|
||||
|
||||
public class CSharpRedirectRenderingConventions : CSharpRenderingConventions
|
||||
{
|
||||
private readonly string _redirectWriter;
|
||||
|
||||
public CSharpRedirectRenderingConventions(string redirectWriter, CSharpCodeWriter writer) : base(writer)
|
||||
{
|
||||
_redirectWriter = redirectWriter;
|
||||
}
|
||||
|
||||
public override string StartWriteMethod => "WriteTo(" + _redirectWriter + ", " /* ORIGINAL: WriteToMethodName */;
|
||||
|
||||
public override string StartWriteLiteralMethod => "WriteLiteralTo(" + _redirectWriter + ", " /* ORIGINAL: WriteLiteralToMethodName */;
|
||||
|
||||
|
||||
public override string StartBeginWriteAttributeMethod => "BeginWriteAttributeTo(" + _redirectWriter + ", " /* ORIGINAL: BeginWriteAttributeToMethodName */;
|
||||
|
||||
|
||||
public override string StartWriteAttributeValueMethod => "WriteAttributeValueTo(" + _redirectWriter + ", " /* ORIGINAL: WriteAttributeValueToMethodName */;
|
||||
|
||||
|
||||
public override string StartEndWriteAttributeMethod => "EndWriteAttributeTo(" + _redirectWriter /* ORIGINAL: EndWriteAttributeToMethodName */;
|
||||
}
|
||||
|
||||
public class CSharpRenderingConventions
|
||||
{
|
||||
public CSharpRenderingConventions(CSharpCodeWriter writer)
|
||||
{
|
||||
Writer = writer;
|
||||
}
|
||||
|
||||
protected CSharpCodeWriter Writer { get; }
|
||||
|
||||
public virtual string StartWriteMethod => "Write(" /* ORIGINAL: WriteMethodName */;
|
||||
|
||||
public virtual string StartWriteLiteralMethod => "WriteLiteral(" /* ORIGINAL: WriteLiteralMethodName */;
|
||||
|
||||
public virtual string StartBeginWriteAttributeMethod => "BeginWriteAttribute(" /* ORIGINAL: BeginWriteAttributeMethodName */;
|
||||
|
||||
public virtual string StartWriteAttributeValueMethod => "WriteAttributeValue(" /* ORIGINAL: WriteAttributeValueMethodName */;
|
||||
|
||||
public virtual string StartEndWriteAttributeMethod => "EndWriteAttribute(" /* ORIGINAL: EndWriteAttributeMethodName */;
|
||||
}
|
||||
|
||||
public class CSharpRenderingContext
|
||||
{
|
||||
private CSharpRenderingConventions _renderingConventions;
|
||||
|
||||
public ICollection<DirectiveDescriptor> Directives { get; set; }
|
||||
|
||||
public CSharpCodeWriter Writer { get; set; }
|
||||
|
||||
public CSharpRenderingConventions RenderingConventions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderingConventions == null)
|
||||
{
|
||||
_renderingConventions = new CSharpRenderingConventions(Writer);
|
||||
}
|
||||
|
||||
return _renderingConventions;
|
||||
}
|
||||
set
|
||||
{
|
||||
_renderingConventions = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<RazorError> Errors { get; } = new List<RazorError>();
|
||||
|
||||
public RazorSourceDocument SourceDocument { get; set; }
|
||||
|
||||
public RazorParserOptions Options { get; set; }
|
||||
}
|
||||
|
||||
public class LinePragmaWriter : IDisposable
|
||||
{
|
||||
private readonly CSharpCodeWriter _writer;
|
||||
private readonly int _startIndent;
|
||||
|
||||
public LinePragmaWriter(CSharpCodeWriter writer, MappingLocation documentLocation)
|
||||
{
|
||||
if (writer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(writer));
|
||||
}
|
||||
|
||||
_writer = writer;
|
||||
_startIndent = _writer.CurrentIndent;
|
||||
_writer.ResetIndent();
|
||||
_writer.WriteLineNumberDirective(documentLocation, documentLocation.FilePath);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Need to add an additional line at the end IF there wasn't one already written.
|
||||
// This is needed to work with the C# editor's handling of #line ...
|
||||
var builder = _writer.Builder;
|
||||
var endsWithNewline = builder.Length > 0 && builder[builder.Length - 1] == '\n';
|
||||
|
||||
// Always write at least 1 empty line to potentially separate code from pragmas.
|
||||
_writer.WriteLine();
|
||||
|
||||
// Check if the previous empty line wasn't enough to separate code from pragmas.
|
||||
if (!endsWithNewline)
|
||||
{
|
||||
_writer.WriteLine();
|
||||
}
|
||||
|
||||
_writer
|
||||
.WriteLineDefaultDirective()
|
||||
.WriteLineHiddenDirective()
|
||||
.SetIndent(_startIndent);
|
||||
}
|
||||
}
|
||||
|
||||
public class PageStructureCSharpRenderer : RazorIRNodeWalker
|
||||
{
|
||||
protected readonly CSharpRenderingContext Context;
|
||||
|
||||
public PageStructureCSharpRenderer(CSharpRenderingContext context)
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
|
||||
public override void VisitNamespace(NamespaceDeclarationIRNode node)
|
||||
{
|
||||
Context.Writer
|
||||
.Write("namespace ")
|
||||
.WriteLine(node.Content);
|
||||
|
||||
using (Context.Writer.BuildScope())
|
||||
{
|
||||
Context.Writer.WriteLineHiddenDirective();
|
||||
VisitDefault(node);
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitRazorMethodDeclaration(RazorMethodDeclarationIRNode node)
|
||||
{
|
||||
Context.Writer
|
||||
.WriteLine("#pragma warning disable 1998")
|
||||
.Write(node.AccessModifier)
|
||||
.Write(" ");
|
||||
|
||||
if (node.Modifiers != null)
|
||||
{
|
||||
for (var i = 0; i < node.Modifiers.Count; i++)
|
||||
{
|
||||
Context.Writer.Write(node.Modifiers[i]);
|
||||
|
||||
if (i + 1 < node.Modifiers.Count)
|
||||
{
|
||||
Context.Writer.Write(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context.Writer
|
||||
.Write(" ")
|
||||
.Write(node.ReturnType)
|
||||
.Write(" ")
|
||||
.Write(node.Name)
|
||||
.WriteLine("()");
|
||||
|
||||
using (Context.Writer.BuildScope())
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
||||
Context.Writer.WriteLine("#pragma warning restore 1998");
|
||||
}
|
||||
|
||||
public override void VisitClass(ClassDeclarationIRNode node)
|
||||
{
|
||||
Context.Writer
|
||||
.Write(node.AccessModifier)
|
||||
.Write(" class ")
|
||||
.Write(node.Name);
|
||||
|
||||
if (node.BaseType != null || node.Interfaces != null)
|
||||
{
|
||||
Context.Writer.Write(" : ");
|
||||
}
|
||||
|
||||
if (node.BaseType != null)
|
||||
{
|
||||
Context.Writer.Write(node.BaseType);
|
||||
|
||||
if (node.Interfaces != null)
|
||||
{
|
||||
Context.Writer.WriteParameterSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
if (node.Interfaces != null)
|
||||
{
|
||||
for (var i = 0; i < node.Interfaces.Count; i++)
|
||||
{
|
||||
Context.Writer.Write(node.Interfaces[i]);
|
||||
|
||||
if (i + 1 < node.Interfaces.Count)
|
||||
{
|
||||
Context.Writer.WriteParameterSeparator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context.Writer.WriteLine();
|
||||
|
||||
using (Context.Writer.BuildScope())
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CSharpRenderer : PageStructureCSharpRenderer
|
||||
{
|
||||
public CSharpRenderer(CSharpRenderingContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public override void VisitChecksum(ChecksumIRNode node)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(node.Bytes))
|
||||
{
|
||||
Context.Writer
|
||||
.Write("#pragma checksum \"")
|
||||
.Write(node.Filename)
|
||||
.Write("\" \"")
|
||||
.Write(node.Guid)
|
||||
.Write("\" \"")
|
||||
.Write(node.Bytes)
|
||||
.WriteLine("\"");
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitCSharpToken(CSharpTokenIRNode node)
|
||||
{
|
||||
Context.Writer.Write(node.Content);
|
||||
}
|
||||
|
||||
public override void VisitHtml(HtmlContentIRNode node)
|
||||
{
|
||||
const int MaxStringLiteralLength = 1024;
|
||||
|
||||
var charactersConsumed = 0;
|
||||
|
||||
// Render the string in pieces to avoid Roslyn OOM exceptions at compile time: https://github.com/aspnet/External/issues/54
|
||||
while (charactersConsumed < node.Content.Length)
|
||||
{
|
||||
string textToRender;
|
||||
if (node.Content.Length <= MaxStringLiteralLength)
|
||||
{
|
||||
textToRender = node.Content;
|
||||
}
|
||||
else
|
||||
{
|
||||
var charactersToSubstring = Math.Min(MaxStringLiteralLength, node.Content.Length - charactersConsumed);
|
||||
textToRender = node.Content.Substring(charactersConsumed, charactersToSubstring);
|
||||
}
|
||||
|
||||
Context.Writer
|
||||
.Write(Context.RenderingConventions.StartWriteLiteralMethod)
|
||||
.WriteStringLiteral(textToRender)
|
||||
.WriteEndMethodInvocation();
|
||||
|
||||
charactersConsumed += textToRender.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitCSharpExpression(CSharpExpressionIRNode node)
|
||||
{
|
||||
IDisposable linePragmaScope = null;
|
||||
if (node.SourceRange != null)
|
||||
{
|
||||
linePragmaScope = new LinePragmaWriter(Context.Writer, node.SourceRange);
|
||||
}
|
||||
|
||||
var padding = BuildOffsetPadding(Context.RenderingConventions.StartWriteMethod.Length, node.SourceRange);
|
||||
Context.Writer
|
||||
.Write(padding)
|
||||
.Write(Context.RenderingConventions.StartWriteMethod);
|
||||
|
||||
VisitDefault(node);
|
||||
|
||||
Context.Writer.WriteEndMethodInvocation();
|
||||
|
||||
linePragmaScope?.Dispose();
|
||||
}
|
||||
|
||||
public override void VisitUsingStatement(UsingStatementIRNode node)
|
||||
{
|
||||
Context.Writer.WriteUsing(node.Content);
|
||||
}
|
||||
|
||||
public override void VisitHtmlAttribute(HtmlAttributeIRNode node)
|
||||
{
|
||||
var valuePieceCount = node
|
||||
.Children
|
||||
.Count(child => child is HtmlAttributeValueIRNode || child is CSharpAttributeValueIRNode);
|
||||
var prefixLocation = node.SourceRange.AbsoluteIndex;
|
||||
var suffixLocation = node.SourceRange.AbsoluteIndex + node.SourceRange.ContentLength - node.Suffix.Length;
|
||||
Context.Writer
|
||||
.Write(Context.RenderingConventions.StartBeginWriteAttributeMethod)
|
||||
.WriteStringLiteral(node.Name)
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(node.Prefix)
|
||||
.WriteParameterSeparator()
|
||||
.Write(prefixLocation.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(node.Suffix)
|
||||
.WriteParameterSeparator()
|
||||
.Write(suffixLocation.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteParameterSeparator()
|
||||
.Write(valuePieceCount.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteEndMethodInvocation();
|
||||
|
||||
VisitDefault(node);
|
||||
|
||||
Context.Writer
|
||||
.Write(Context.RenderingConventions.StartEndWriteAttributeMethod)
|
||||
.WriteEndMethodInvocation();
|
||||
}
|
||||
|
||||
public override void VisitHtmlAttributeValue(HtmlAttributeValueIRNode node)
|
||||
{
|
||||
var prefixLocation = node.SourceRange.AbsoluteIndex;
|
||||
var valueLocation = node.SourceRange.AbsoluteIndex + node.Prefix.Length;
|
||||
var valueLength = node.SourceRange.ContentLength;
|
||||
Context.Writer
|
||||
.Write(Context.RenderingConventions.StartWriteAttributeValueMethod)
|
||||
.WriteStringLiteral(node.Prefix)
|
||||
.WriteParameterSeparator()
|
||||
.Write(prefixLocation.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(node.Content)
|
||||
.WriteParameterSeparator()
|
||||
.Write(valueLocation.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteParameterSeparator()
|
||||
.Write(valueLength.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteParameterSeparator()
|
||||
.WriteBooleanLiteral(true)
|
||||
.WriteEndMethodInvocation();
|
||||
}
|
||||
|
||||
public override void VisitCSharpAttributeValue(CSharpAttributeValueIRNode node)
|
||||
{
|
||||
const string ValueWriterName = "__razor_attribute_value_writer";
|
||||
|
||||
var expressionValue = node.Children.First() as CSharpExpressionIRNode;
|
||||
var linePragma = expressionValue != null ? new LinePragmaWriter(Context.Writer, node.SourceRange) : null;
|
||||
var prefixLocation = node.SourceRange.AbsoluteIndex;
|
||||
var valueLocation = node.SourceRange.AbsoluteIndex + node.Prefix.Length;
|
||||
var valueLength = node.SourceRange.ContentLength - node.Prefix.Length;
|
||||
Context.Writer
|
||||
.Write(Context.RenderingConventions.StartWriteAttributeValueMethod)
|
||||
.WriteStringLiteral(node.Prefix)
|
||||
.WriteParameterSeparator()
|
||||
.Write(prefixLocation.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteParameterSeparator();
|
||||
|
||||
if (expressionValue != null)
|
||||
{
|
||||
Debug.Assert(node.Children.Count == 1);
|
||||
|
||||
RenderExpressionInline(expressionValue, Context);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not an expression; need to buffer the result.
|
||||
Context.Writer.WriteStartNewObject("HelperResult" /* ORIGINAL: TemplateTypeName */);
|
||||
|
||||
var initialRenderingConventions = Context.RenderingConventions;
|
||||
var redirectConventions = new CSharpRedirectRenderingConventions(ValueWriterName, Context.Writer);
|
||||
Context.RenderingConventions = redirectConventions;
|
||||
using (Context.Writer.BuildAsyncLambda(endLine: false, parameterNames: ValueWriterName))
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
Context.RenderingConventions = initialRenderingConventions;
|
||||
|
||||
Context.Writer.WriteEndMethodInvocation(false);
|
||||
}
|
||||
|
||||
Context.Writer
|
||||
.WriteParameterSeparator()
|
||||
.Write(valueLocation.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteParameterSeparator()
|
||||
.Write(valueLength.ToString(CultureInfo.InvariantCulture))
|
||||
.WriteParameterSeparator()
|
||||
.WriteBooleanLiteral(false)
|
||||
.WriteEndMethodInvocation();
|
||||
|
||||
linePragma?.Dispose();
|
||||
}
|
||||
|
||||
public override void VisitDirective(DirectiveIRNode node)
|
||||
{
|
||||
if (string.Equals(node.Name, CSharpCodeParser.SectionDirectiveDescriptor.Name, StringComparison.Ordinal))
|
||||
{
|
||||
const string SectionWriterName = "__razor_section_writer";
|
||||
|
||||
Context.Writer
|
||||
.WriteStartMethodInvocation("DefineSection" /* ORIGINAL: DefineSectionMethodName */)
|
||||
.WriteStringLiteral(node.Tokens.FirstOrDefault()?.Content)
|
||||
.WriteParameterSeparator();
|
||||
|
||||
var initialRenderingConventions = Context.RenderingConventions;
|
||||
var redirectConventions = new CSharpRedirectRenderingConventions(SectionWriterName, Context.Writer);
|
||||
Context.RenderingConventions = redirectConventions;
|
||||
using (Context.Writer.BuildAsyncLambda(endLine: false, parameterNames: SectionWriterName))
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
Context.RenderingConventions = initialRenderingConventions;
|
||||
|
||||
Context.Writer.WriteEndMethodInvocation();
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitCSharpStatement(CSharpStatementIRNode node)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(node.Content))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.SourceRange != null)
|
||||
{
|
||||
using (new LinePragmaWriter(Context.Writer, node.SourceRange))
|
||||
{
|
||||
var padding = BuildOffsetPadding(0, node.SourceRange);
|
||||
Context.Writer
|
||||
.Write(padding)
|
||||
.WriteLine(node.Content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.Writer.WriteLine(node.Content);
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitTemplate(TemplateIRNode node)
|
||||
{
|
||||
const string ItemParameterName = "item";
|
||||
const string TemplateWriterName = "__razor_template_writer";
|
||||
|
||||
Context.Writer
|
||||
.Write(ItemParameterName).Write(" => ")
|
||||
.WriteStartNewObject("HelperResult" /* ORIGINAL: TemplateTypeName */);
|
||||
|
||||
var initialRenderingConventions = Context.RenderingConventions;
|
||||
var redirectConventions = new CSharpRedirectRenderingConventions(TemplateWriterName, Context.Writer);
|
||||
Context.RenderingConventions = redirectConventions;
|
||||
using (Context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName))
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
Context.RenderingConventions = initialRenderingConventions;
|
||||
|
||||
Context.Writer.WriteEndMethodInvocation(endLine: false);
|
||||
}
|
||||
|
||||
private static int CalculateExpressionPadding(MappingLocation sourceRange, CSharpRenderingContext context)
|
||||
{
|
||||
var spaceCount = 0;
|
||||
for (var i = sourceRange.AbsoluteIndex - 1; i >= 0; i--)
|
||||
{
|
||||
var @char = context.SourceDocument[i];
|
||||
if (@char == '\n' || @char == '\r')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (@char == '\t')
|
||||
{
|
||||
spaceCount += context.Options.TabSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
spaceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return spaceCount;
|
||||
}
|
||||
|
||||
private string BuildOffsetPadding(int generatedOffset, MappingLocation sourceRange)
|
||||
{
|
||||
var basePadding = CalculateExpressionPadding(sourceRange, Context);
|
||||
var resolvedPadding = Math.Max(basePadding - generatedOffset, 0);
|
||||
|
||||
if (Context.Options.IsIndentingWithTabs)
|
||||
{
|
||||
var spaces = resolvedPadding % Context.Options.TabSize;
|
||||
var tabs = resolvedPadding / Context.Options.TabSize;
|
||||
|
||||
return new string('\t', tabs) + new string(' ', spaces);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new string(' ', resolvedPadding);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RenderExpressionInline(RazorIRNode node, CSharpRenderingContext context)
|
||||
{
|
||||
if (node is CSharpTokenIRNode)
|
||||
{
|
||||
context.Writer.Write(((CSharpTokenIRNode)node).Content);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < node.Children.Count; i++)
|
||||
{
|
||||
RenderExpressionInline(node.Children[i], context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||
|
||||
|
|
@ -9,12 +11,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
internal class DefaultRazorIRLoweringPhase : RazorEnginePhaseBase, IRazorIRLoweringPhase
|
||||
{
|
||||
private IRazorConfigureParserFeature[] _parserOptionsCallbacks;
|
||||
|
||||
protected override void OnIntialized()
|
||||
{
|
||||
_parserOptionsCallbacks = Engine.Features.OfType<IRazorConfigureParserFeature>().ToArray();
|
||||
}
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
ThrowForMissingDependency(syntaxTree);
|
||||
|
||||
var visitor = new Visitor();
|
||||
var visitor = new Visitor(codeDocument, syntaxTree.Options);
|
||||
|
||||
visitor.VisitBlock(syntaxTree.Root);
|
||||
|
||||
|
|
@ -25,16 +34,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
private class Visitor : ParserVisitor
|
||||
{
|
||||
private readonly Stack<RazorIRBuilder> _builders;
|
||||
private readonly RazorParserOptions _options;
|
||||
private readonly RazorCodeDocument _codeDocument;
|
||||
|
||||
public Visitor()
|
||||
public Visitor(RazorCodeDocument codeDocument, RazorParserOptions options)
|
||||
{
|
||||
_codeDocument = codeDocument;
|
||||
_options = options;
|
||||
_builders = new Stack<RazorIRBuilder>();
|
||||
var document = RazorIRBuilder.Document();
|
||||
_builders.Push(document);
|
||||
|
||||
var checksum = ChecksumIRNode.Create(codeDocument.Source);
|
||||
Builder.Add(checksum);
|
||||
|
||||
Namespace = new NamespaceDeclarationIRNode();
|
||||
Builder.Push(Namespace);
|
||||
|
||||
foreach (var namespaceImport in options.NamespaceImports)
|
||||
{
|
||||
var @using = new UsingStatementIRNode()
|
||||
{
|
||||
Content = namespaceImport,
|
||||
Parent = Namespace,
|
||||
};
|
||||
|
||||
Builder.Add(@using);
|
||||
}
|
||||
|
||||
Class = new ClassDeclarationIRNode();
|
||||
Builder.Push(Class);
|
||||
|
||||
|
|
@ -62,7 +89,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
Name = chunkGenerator.Name,
|
||||
Prefix = chunkGenerator.Prefix,
|
||||
Suffix = chunkGenerator.Suffix,
|
||||
SourceRange = new MappingLocation(block.Start, block.Length),
|
||||
SourceRange = BuildSourceRangeFromNode(block),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +107,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
Builder.Push(new CSharpAttributeValueIRNode()
|
||||
{
|
||||
Prefix = chunkGenerator.Prefix,
|
||||
SourceRange = new MappingLocation(block.Start, block.Length),
|
||||
SourceRange = BuildSourceRangeFromNode(block),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +122,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
Prefix = chunkGenerator.Prefix,
|
||||
Content = chunkGenerator.Value,
|
||||
SourceRange = new MappingLocation(span.Start, span.Length),
|
||||
SourceRange = BuildSourceRangeFromNode(span),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -126,20 +153,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
if (expressionNode.Children.Count > 0)
|
||||
{
|
||||
var sourceRangeStart = expressionNode.Children[0].SourceRange;
|
||||
var contentLength = 0;
|
||||
|
||||
for (var i = 0; i < expressionNode.Children.Count; i++)
|
||||
{
|
||||
contentLength += expressionNode.Children[i].SourceRange.ContentLength;
|
||||
}
|
||||
var sourceRangeStart = expressionNode
|
||||
.Children
|
||||
.FirstOrDefault(child => child.SourceRange != null)
|
||||
?.SourceRange;
|
||||
var contentLength = expressionNode.Children.Sum(child => child.SourceRange?.ContentLength ?? 0);
|
||||
|
||||
expressionNode.SourceRange = new MappingLocation(
|
||||
sourceRangeStart.AbsoluteIndex,
|
||||
sourceRangeStart.LineIndex,
|
||||
sourceRangeStart.CharacterIndex,
|
||||
contentLength,
|
||||
sourceRangeStart.FilePath);
|
||||
sourceRangeStart.FilePath ?? _codeDocument.Source.Filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -149,7 +174,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
Builder.Add(new CSharpTokenIRNode()
|
||||
{
|
||||
Content = span.Content,
|
||||
SourceRange = new MappingLocation(span.Start, span.Length),
|
||||
SourceRange = BuildSourceRangeFromNode(span),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +183,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
Builder.Add(new CSharpStatementIRNode()
|
||||
{
|
||||
Content = span.Content,
|
||||
SourceRange = new MappingLocation(span.Start, span.Length),
|
||||
SourceRange = BuildSourceRangeFromNode(span),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -175,13 +200,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
Builder.Add(new HtmlContentIRNode()
|
||||
{
|
||||
Content = span.Content,
|
||||
SourceRange = new MappingLocation(span.Start, span.Length),
|
||||
SourceRange = BuildSourceRangeFromNode(span),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitImportSpan(AddImportChunkGenerator chunkGenerator, Span span)
|
||||
{
|
||||
var namespaceImport = chunkGenerator.Namespace.Trim();
|
||||
|
||||
if (_options.NamespaceImports.Contains(namespaceImport, StringComparer.Ordinal))
|
||||
{
|
||||
// Already added by default
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// For prettiness, let's insert the usings before the class declaration.
|
||||
var i = 0;
|
||||
for (; i < Namespace.Children.Count; i++)
|
||||
|
|
@ -194,9 +228,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
var @using = new UsingStatementIRNode()
|
||||
{
|
||||
Content = span.Content,
|
||||
Content = namespaceImport,
|
||||
Parent = Namespace,
|
||||
SourceRange = new MappingLocation(span.Start, span.Length),
|
||||
SourceRange = BuildSourceRangeFromNode(span),
|
||||
};
|
||||
|
||||
Namespace.Children.Insert(i, @using);
|
||||
|
|
@ -208,7 +242,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
Content = span.Content,
|
||||
Descriptor = chunkGenerator.Descriptor,
|
||||
SourceRange = new MappingLocation(span.Start, span.Length),
|
||||
SourceRange = BuildSourceRangeFromNode(span),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -225,6 +259,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
Builder.Pop();
|
||||
}
|
||||
|
||||
private MappingLocation BuildSourceRangeFromNode(SyntaxTreeNode node)
|
||||
{
|
||||
var location = node.Start;
|
||||
var sourceRange = new MappingLocation(
|
||||
location.AbsoluteIndex,
|
||||
location.LineIndex,
|
||||
location.CharacterIndex,
|
||||
node.Length,
|
||||
location.FilePath ?? _codeDocument.Source.Filename);
|
||||
|
||||
return sourceRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 IRazorCSharpLoweringPhase : IRazorEnginePhase
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||
{
|
||||
public class ChecksumIRNode : RazorIRNode
|
||||
{
|
||||
public override IList<RazorIRNode> Children => EmptyArray;
|
||||
|
||||
public override RazorIRNode Parent { get; set; }
|
||||
|
||||
internal override MappingLocation SourceRange { get; set; }
|
||||
|
||||
public string Bytes { get; set; }
|
||||
|
||||
public string Filename { get; set; }
|
||||
|
||||
public string Guid { get; set; }
|
||||
|
||||
public override void Accept(RazorIRNodeVisitor visitor)
|
||||
{
|
||||
visitor.VisitChecksum(this);
|
||||
}
|
||||
|
||||
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
|
||||
{
|
||||
return visitor.VisitChecksum(this);
|
||||
}
|
||||
|
||||
public static ChecksumIRNode Create(RazorSourceDocument sourceDocument)
|
||||
{
|
||||
// See http://msdn.microsoft.com/en-us/library/system.codedom.codechecksumpragma.checksumalgorithmid.aspx
|
||||
const string Sha1AlgorithmId = "{ff1816ec-aa5e-4d10-87f7-6f4963833460}";
|
||||
|
||||
var node = new ChecksumIRNode()
|
||||
{
|
||||
Filename = sourceDocument.Filename,
|
||||
Guid = Sha1AlgorithmId
|
||||
};
|
||||
|
||||
var charBuffer = new char[sourceDocument.Length];
|
||||
sourceDocument.CopyTo(0, charBuffer, 0, sourceDocument.Length);
|
||||
|
||||
var encoder = sourceDocument.Encoding.GetEncoder();
|
||||
var byteCount = encoder.GetByteCount(charBuffer, 0, charBuffer.Length, flush: true);
|
||||
var checksumBytes = new byte[byteCount];
|
||||
encoder.GetBytes(charBuffer, 0, charBuffer.Length, checksumBytes, 0, flush: true);
|
||||
|
||||
using (var hashAlgorithm = SHA1.Create())
|
||||
{
|
||||
var hashedBytes = hashAlgorithm.ComputeHash(checksumBytes);
|
||||
var fileHashBuilder = new StringBuilder(hashedBytes.Length * 2);
|
||||
foreach (var value in hashedBytes)
|
||||
{
|
||||
fileHashBuilder.Append(value.ToString("x2"));
|
||||
}
|
||||
|
||||
node.Bytes = fileHashBuilder.ToString();
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
{
|
||||
}
|
||||
|
||||
public virtual void VisitChecksum(ChecksumIRNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitDirectiveToken(DirectiveTokenIRNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
return default(TResult);
|
||||
}
|
||||
|
||||
public virtual TResult VisitChecksum(ChecksumIRNode node)
|
||||
{
|
||||
return VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual TResult VisitDirectiveToken(DirectiveTokenIRNode node)
|
||||
{
|
||||
return VisitDefault(node);
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
/// <param name="location">The location to generate the line pragma for.</param>
|
||||
/// <param name="file">The file to generate the line pragma for.</param>
|
||||
/// <returns>The current instance of <see cref="CSharpCodeWriter"/>.</returns>
|
||||
public CSharpCodeWriter WriteLineNumberDirective(SourceLocation location, string file)
|
||||
public CSharpCodeWriter WriteLineNumberDirective(MappingLocation location, string file)
|
||||
{
|
||||
if (location.FilePath != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
builder.Phases.Add(new DefaultRazorSyntaxTreePhase());
|
||||
builder.Phases.Add(new DefaultRazorIRLoweringPhase());
|
||||
builder.Phases.Add(new DefaultRazorIRPhase());
|
||||
builder.Phases.Add(new DefaultRazorCSharpLoweringPhase());
|
||||
|
||||
// Syntax Tree passes
|
||||
builder.Features.Add(new DefaultDirectiveSyntaxTreePass());
|
||||
|
|
|
|||
|
|
@ -1,7 +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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
|
|
@ -15,10 +17,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
private RazorParserOptions()
|
||||
{
|
||||
Directives = new List<DirectiveDescriptor>();
|
||||
NamespaceImports = new HashSet<string>(StringComparer.Ordinal) { nameof(System), typeof(Task).Namespace };
|
||||
}
|
||||
|
||||
public bool DesignTimeMode { get; set; }
|
||||
|
||||
public int TabSize { get; set; } = 4;
|
||||
|
||||
public bool IsIndentingWithTabs { get; set; }
|
||||
|
||||
public ICollection<DirectiveDescriptor> Directives { get; }
|
||||
|
||||
public HashSet<string> NamespaceImports { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
public class DefaultRazorCSharpLoweringPhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorCSharpLoweringPhase();
|
||||
|
||||
var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The '{nameof(DefaultRazorCSharpLoweringPhase)}' phase requires a '{nameof(DocumentIRNode)}' " +
|
||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -97,8 +97,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
Assert.Equal("test_directive", directiveSpan.Content);
|
||||
|
||||
var irDocument = document.GetIRDocument();
|
||||
var irNamespace = irDocument.Children[0];
|
||||
var irClass = irNamespace.Children[0];
|
||||
var irNamespace = irDocument.Children[1];
|
||||
var irClass = irNamespace.Children[2];
|
||||
var irMethod = irClass.Children[0];
|
||||
var irDirective = (DirectiveIRNode)irMethod.Children[1];
|
||||
Assert.Equal("test_directive", irDirective.Name);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
|
||||
using Xunit;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||
{
|
||||
|
|
@ -19,8 +20,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
var irDocument = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||
Children(irDocument,
|
||||
n => Assert.IsType<ChecksumIRNode>(n),
|
||||
n => Assert.IsType<NamespaceDeclarationIRNode>(n));
|
||||
var @namespace = irDocument.Children[1];
|
||||
Children(@namespace,
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<ClassDeclarationIRNode>(n));
|
||||
var @class = @namespace.Children[2];
|
||||
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||
var html = SingleChild<HtmlContentIRNode>(method);
|
||||
|
||||
|
|
@ -37,8 +45,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
var irDocument = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||
Children(irDocument,
|
||||
n => Assert.IsType<ChecksumIRNode>(n),
|
||||
n => Assert.IsType<NamespaceDeclarationIRNode>(n));
|
||||
var @namespace = irDocument.Children[1];
|
||||
Children(@namespace,
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<ClassDeclarationIRNode>(n));
|
||||
var @class = @namespace.Children[2];
|
||||
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||
var html = SingleChild<HtmlContentIRNode>(method);
|
||||
|
||||
|
|
@ -60,8 +75,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
var irDocument = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||
Children(irDocument,
|
||||
n => Assert.IsType<ChecksumIRNode>(n),
|
||||
n => Assert.IsType<NamespaceDeclarationIRNode>(n));
|
||||
var @namespace = irDocument.Children[1];
|
||||
Children(@namespace,
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<ClassDeclarationIRNode>(n));
|
||||
var @class = @namespace.Children[2];
|
||||
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||
Children(method,
|
||||
n => Html(
|
||||
|
|
@ -90,8 +112,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
var irDocument = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||
Children(irDocument,
|
||||
n => Assert.IsType<ChecksumIRNode>(n),
|
||||
n => Assert.IsType<NamespaceDeclarationIRNode>(n));
|
||||
var @namespace = irDocument.Children[1];
|
||||
Children(@namespace,
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<ClassDeclarationIRNode>(n));
|
||||
var @class = @namespace.Children[2];
|
||||
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||
Children(method,
|
||||
n => Html(
|
||||
|
|
@ -125,8 +154,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
var irDocument = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||
Children(irDocument,
|
||||
n => Assert.IsType<ChecksumIRNode>(n),
|
||||
n => Assert.IsType<NamespaceDeclarationIRNode>(n));
|
||||
var @namespace = irDocument.Children[1];
|
||||
Children(@namespace,
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<UsingStatementIRNode>(n),
|
||||
n => Assert.IsType<ClassDeclarationIRNode>(n));
|
||||
var @class = @namespace.Children[2];
|
||||
Children(@class,
|
||||
n => Assert.IsType<RazorMethodDeclarationIRNode>(n),
|
||||
n => Assert.IsType<CSharpStatementIRNode>(n));
|
||||
|
|
@ -142,9 +178,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
var irDocument = Lower(codeDocument);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
Children(irDocument,
|
||||
n => Assert.IsType<ChecksumIRNode>(n),
|
||||
n => Assert.IsType<NamespaceDeclarationIRNode>(n));
|
||||
var @namespace = irDocument.Children[1];
|
||||
Children(@namespace,
|
||||
n => Using("using System", n),
|
||||
n => Using("System", n),
|
||||
n => Using(typeof(Task).Namespace, n),
|
||||
n => Assert.IsType<ClassDeclarationIRNode>(n));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
var writer = new CSharpCodeWriter();
|
||||
var expected = $"#line 5 \"{filePath}\"" + writer.NewLine;
|
||||
var sourceLocation = new SourceLocation(10, 4, 3);
|
||||
var mappingLocation = new MappingLocation(sourceLocation, 9);
|
||||
|
||||
// Act
|
||||
writer.WriteLineNumberDirective(sourceLocation, filePath);
|
||||
writer.WriteLineNumberDirective(mappingLocation, filePath);
|
||||
var code = writer.GenerateCode();
|
||||
|
||||
// Assert
|
||||
|
|
@ -35,9 +36,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
var writer = new CSharpCodeWriter();
|
||||
var expected = $"#line 5 \"{sourceLocationFilePath}\"" + writer.NewLine;
|
||||
var sourceLocation = new SourceLocation(sourceLocationFilePath, 10, 4, 3);
|
||||
var mappingLocation = new MappingLocation(sourceLocation, 9);
|
||||
|
||||
// Act
|
||||
writer.WriteLineNumberDirective(sourceLocation, filePath);
|
||||
writer.WriteLineNumberDirective(mappingLocation, filePath);
|
||||
var code = writer.GenerateCode();
|
||||
|
||||
// Assert
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
phase => Assert.IsType<DefaultRazorParsingPhase>(phase),
|
||||
phase => Assert.IsType<DefaultRazorSyntaxTreePhase>(phase),
|
||||
phase => Assert.IsType<DefaultRazorIRLoweringPhase>(phase),
|
||||
phase => Assert.IsType<DefaultRazorIRPhase>(phase));
|
||||
phase => Assert.IsType<DefaultRazorIRPhase>(phase),
|
||||
phase => Assert.IsType<DefaultRazorCSharpLoweringPhase>(phase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
Document -
|
||||
Checksum -
|
||||
NamespaceDeclaration - -
|
||||
UsingStatement - - System
|
||||
UsingStatement - - System.Threading.Tasks
|
||||
ClassDeclaration - - - - -
|
||||
RazorMethodDeclaration - - - - -
|
||||
HtmlContent - (0:0,0 [0] ) -
|
||||
HtmlContent - (0:0,0 [0] TestFiles/IntegrationTests/BasicIntegrationTest/CustomDirective.cshtml) -
|
||||
Directive - - test_directive
|
||||
HtmlContent - (15:0,15 [0] ) -
|
||||
HtmlContent - (15:0,15 [0] TestFiles/IntegrationTests/BasicIntegrationTest/CustomDirective.cshtml) -
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
Document -
|
||||
Checksum -
|
||||
NamespaceDeclaration - -
|
||||
UsingStatement - - System
|
||||
UsingStatement - - System.Threading.Tasks
|
||||
ClassDeclaration - - - - -
|
||||
RazorMethodDeclaration - - - - -
|
||||
HtmlContent - (0:0,0 [0] ) -
|
||||
HtmlContent - (0:0,0 [0] TestFiles/IntegrationTests/BasicIntegrationTest/Empty.cshtml) -
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
Document -
|
||||
Checksum -
|
||||
NamespaceDeclaration - -
|
||||
UsingStatement - - System
|
||||
UsingStatement - - System.Threading.Tasks
|
||||
ClassDeclaration - - - - -
|
||||
RazorMethodDeclaration - - - - -
|
||||
HtmlContent - (0:0,0 [13] ) - Hello, World!
|
||||
HtmlContent - (0:0,0 [13] TestFiles/IntegrationTests/BasicIntegrationTest/HelloWorld.cshtml) - Hello, World!
|
||||
|
|
|
|||
Loading…
Reference in New Issue