aspnetcore/src/Microsoft.AspNetCore.Razor..../DefaultRazorRuntimeCSharpLo...

266 lines
11 KiB
C#

// 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.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 DefaultRazorRuntimeCSharpLoweringPhase : RazorCSharpLoweringPhaseBase
{
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.VisitDocument(irDocument);
var csharpDocument = new RazorCSharpDocument()
{
GeneratedCode = renderingContext.Writer.GenerateCode(),
LineMappings = renderingContext.LineMappings,
};
codeDocument.SetCSharpDocument(csharpDocument);
}
private 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);
Context.Writer.Write(padding);
}
Context.Writer.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 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);
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);
}
}
}
}