Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Commit

Permalink
Change string rendering to be chunked.
Browse files Browse the repository at this point in the history
- Roslyn currently has an issue where too large of strings result in out of memory exceptions at compile time. To combat this I broke down literal strings into 1k pieces each resulting in their own `WriteLiteral`/`WriteLiteralTo` calls.
- Added tests to validate large string rendering.

#614
  • Loading branch information
NTaylorMullen committed Jan 5, 2016
1 parent 37eca62 commit 4db32ec
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 59 deletions.
17 changes: 0 additions & 17 deletions src/Microsoft.AspNet.Razor/CodeGenerators/CSharpCodeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -470,23 +470,6 @@ private void WriteCStyleStringLiteral(string literal)
Write(literal[i].ToString());
break;
}
if (i > 0 && i % 80 == 0)
{
// If current character is a high surrogate and the following
// character is a low surrogate, don't break them.
// Otherwise when we write the string to a file, we might lose
// the characters.
if (Char.IsHighSurrogate(literal[i])
&& (i < literal.Length - 1)
&& Char.IsLowSurrogate(literal[i + 1]))
{
Write(literal[++i].ToString());
}

Write("\" +");
Write(NewLine);
Write("\"");
}
}
Write("\"");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using Microsoft.AspNet.Razor.Chunks;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;

Expand All @@ -16,6 +15,7 @@ public class CSharpCodeVisitor : CodeVisitor<CSharpCodeWriter>
private const string ItemParameterName = "item";
private const string ValueWriterName = "__razor_attribute_value_writer";
private const string TemplateWriterName = "__razor_template_writer";
private static readonly int MaxStringLiteralLength = 499; // Max 1k string size despite x86/x64

private CSharpPaddingBuilder _paddingBuilder;
private CSharpTagHelperCodeRenderer _tagHelperCodeRenderer;
Expand Down Expand Up @@ -135,17 +135,7 @@ protected override void Visit(ParentLiteralChunk chunk)
Writer.WriteStartInstrumentationContext(Context, start, text.Length, isLiteral: true);
}

if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
RenderPreWriteStart();
}

Writer.WriteStringLiteral(text);

if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
Writer.WriteEndMethodInvocation();
}
RenderChunkedWriteLiteral(text);

if (Context.Host.EnableInstrumentation)
{
Expand All @@ -166,17 +156,7 @@ protected override void Visit(LiteralChunk chunk)
Writer.WriteStartInstrumentationContext(Context, chunk.Association, isLiteral: true);
}

if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
RenderPreWriteStart();
}

Writer.WriteStringLiteral(chunk.Text);

if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
Writer.WriteEndMethodInvocation();
}
RenderChunkedWriteLiteral(chunk.Text);

if (Context.Host.EnableInstrumentation)
{
Expand Down Expand Up @@ -581,6 +561,31 @@ private CSharpCodeWriter RenderPreWriteStart()
return RenderPreWriteStart(Writer, Context);
}

private void RenderChunkedWriteLiteral(string text)
{
var charactersRendered = 0;

// Render the string in pieces to avoid Roslyn OOM exceptions at compile time:
// https://github.com/dotnet/roslyn/issues/7398
while (charactersRendered < text.Length)
{
if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
RenderPreWriteStart();
}

var charactersToSubstring = Math.Min(MaxStringLiteralLength, text.Length - charactersRendered);
var textToRender = text.Substring(charactersRendered, charactersToSubstring);
Writer.WriteStringLiteral(textToRender);
charactersRendered += textToRender.Length;

if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
Writer.WriteEndMethodInvocation();
}
}
}

public static CSharpCodeWriter RenderPreWriteStart(CSharpCodeWriter writer, CodeGeneratorContext context)
{
if (!string.IsNullOrEmpty(context.TargetWriterName))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public void ConstructorAllowsEmptyRootNamespaceName()
}

[Theory]
[InlineData("StringLiterals")]
[InlineData("NestedCSharp")]
[InlineData("NullConditionalExpressions")]
[InlineData("NestedCodeBlocks")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ public Await()
public override async Task ExecuteAsync()
{
Instrumentation.BeginContext(91, 100, true);
WriteLiteral("\r\n<section>\r\n <h1>Basic Asynchronous Expression Test</h1>\r\n <p>Basic Asynch" +
"ronous Expression: ");
WriteLiteral("\r\n<section>\r\n <h1>Basic Asynchronous Expression Test</h1>\r\n <p>Basic Asynchronous Expression: ");
Instrumentation.EndContext();
Instrumentation.BeginContext(192, 11, false);
#line 10 "Await.cshtml"
Expand Down Expand Up @@ -83,8 +82,7 @@ public override async Task ExecuteAsync()
#line hidden
Instrumentation.EndContext();
Instrumentation.BeginContext(453, 124, true);
WriteLiteral("</p>\r\n</section>\r\n\r\n<section>\r\n <h1>Advanced Asynchronous Expression Test</h1>" +
"\r\n <p>Advanced Asynchronous Expression: ");
WriteLiteral("</p>\r\n</section>\r\n\r\n<section>\r\n <h1>Advanced Asynchronous Expression Test</h1>\r\n <p>Advanced Asynchronous Expression: ");
Instrumentation.EndContext();
Instrumentation.BeginContext(578, 15, false);
#line 19 "Await.cshtml"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ public BasicTagHelpers.RemoveTagHelper()
public override async Task ExecuteAsync()
{
Instrumentation.BeginContext(70, 187, true);
WriteLiteral("\r\n<div class=\"randomNonTagHelperAttribute\">\r\n <p class=\"Hello World\">\r\n " +
" <p></p>\r\n <input type=\"text\" />\r\n <input type=\"checkbox\" checked=" +
"\"true\"/>\r\n </p>\r\n</div>");
WriteLiteral("\r\n<div class=\"randomNonTagHelperAttribute\">\r\n <p class=\"Hello World\">\r\n <p></p>\r\n <input type=\"text\" />\r\n <input type=\"checkbox\" checked=\"true\"/>\r\n </p>\r\n</div>");
Instrumentation.EndContext();
}
#pragma warning restore 1998
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ public override async Task ExecuteAsync()
#line hidden
EndWriteAttribute();
Instrumentation.BeginContext(488, 152, true);
WriteLiteral(" type=\"text/javascript\"></script>\r\n <script src=\"http://ajax.aspnetcdn.com/aja" +
"x/jquery.ui/1.8.16/jquery-ui.min.js\" type=\"text/javascript\"></script>\r\n");
WriteLiteral(" type=\"text/javascript\"></script>\r\n <script src=\"http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.16/jquery-ui.min.js\" type=\"text/javascript\"></script>\r\n");
Instrumentation.EndContext();
#line 15 "ConditionalAttributes.cshtml"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ public override async Task ExecuteAsync()
{
__tagHelperRunner = __tagHelperRunner ?? new global::Microsoft.AspNet.Razor.Runtime.TagHelperRunner();
Instrumentation.BeginContext(31, 106, true);
WriteLiteral("\r\n<script type=\"text/html\">\r\n <div data-animation=\"fade\" class=\"randomNonTagHe" +
"lperAttribute\">\r\n ");
WriteLiteral("\r\n<script type=\"text/html\">\r\n <div data-animation=\"fade\" class=\"randomNonTagHelperAttribute\">\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", global::Microsoft.AspNet.Razor.TagHelpers.TagMode.StartTagAndEndTag, "test", async() => {
Instrumentation.BeginContext(178, 2, true);
Expand All @@ -46,8 +45,7 @@ public override async Task ExecuteAsync()
#line hidden
Instrumentation.BeginContext(223, 84, true);
WriteLiteral(" <script id=\"nestedScriptTag\" type=\"text/html\">\r\n " +
" ");
WriteLiteral(" <script id=\"nestedScriptTag\" type=\"text/html\">\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", global::Microsoft.AspNet.Razor.TagHelpers.TagMode.StartTagOnly, "test", async() => {
}
Expand Down Expand Up @@ -90,8 +88,7 @@ public override async Task ExecuteAsync()
#line hidden
Instrumentation.BeginContext(437, 129, true);
WriteLiteral(" <script type=\"text/javascript\">\r\n var tag = \'<input ch" +
"ecked=\"true\">\';\r\n </script>\r\n ");
WriteLiteral(" <script type=\"text/javascript\">\r\n var tag = \'<input checked=\"true\">\';\r\n </script>\r\n ");
Instrumentation.EndContext();
}
, StartTagHelperWritingScope, EndTagHelperWritingScope);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#pragma checksum "StringLiterals.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "69e97ad2cecc829111c1a2ed6b391c9ff7b781ca"
namespace TestOutput
{
using System;
using System.Threading.Tasks;

public class StringLiterals
{
#line hidden
public StringLiterals()
{
}

#pragma warning disable 1998
public override async Task ExecuteAsync()
{
Instrumentation.BeginContext(0, 1005, true);
WriteLiteral(@"<p>This is line 1</p>
<p>This is line 2</p>
<p>This is line 3</p>
<p>This is line 4</p>
<p>This is line 5</p>
<p>This is line 6</p>
<p>This is line 7</p>
<p>This is line 8</p>
<p>This is line 9</p>
<p>This is line 10</p>
<p>This is line 11</p>
<p>This is line 12</p>
<p>This is line 13</p>
<p>This is line 14</p>
<p>This is line 15</p>
<p>This is line 16</p>
<p>This is line 17</p>
<p>This is line 18</p>
<p>This is line 19</p>
<p>This is line 20</p>
<p>This is line 21</p>
<p>T");
WriteLiteral(@"his is line 22</p>
<p>This is line 23</p>
<p>This is line 24</p>
<p>This is line 25</p>
<p>This is line 26</p>
<p>This is line 27</p>
<p>This is line 28</p>
<p>This is line 29</p>
<p>This is line 30</p>
<p>This is line 31</p>
<p>This is line 32</p>
<p>This is line 33</p>
<p>This is line 34</p>
<p>This is line 35</p>
<p>This is line 36</p>
<p>This is line 37</p>
<p>This is line 38</p>
<p>This is line 39</p>
<p>This is line 40</p>
<p>This is line 41</p>
<p>This is line 42</p><");
WriteLiteral("br>\r\n\r\n");
Instrumentation.EndContext();
DefineSection("WriteLiteralsToInHere", async(__razor_template_writer) => {
Instrumentation.BeginContext(1037, 1463, true);
WriteLiteralTo(__razor_template_writer, @"
<p>This is line 1 nested</p>
<p>This is line 2 nested</p>
<p>This is line 3 nested</p>
<p>This is line 4 nested</p>
<p>This is line 5 nested</p>
<p>This is line 6 nested</p>
<p>This is line 7 nested</p>
<p>This is line 8 nested</p>
<p>This is line 9 nested</p>
<p>This is line 10 nested</p>
<p>This is line 11 nested</p>
<p>This is line 12 nested</p>
<p>This is line 13 nested</p>
<p>This is line 14 nested</p>
<p>This is l");
WriteLiteralTo(__razor_template_writer, @"ine 15 nested</p>
<p>This is line 16 nested</p>
<p>This is line 17 nested</p>
<p>This is line 18 nested</p>
<p>This is line 19 nested</p>
<p>This is line 20 nested</p>
<p>This is line 21 nested</p>
<p>This is line 22 nested</p>
<p>This is line 23 nested</p>
<p>This is line 24 nested</p>
<p>This is line 25 nested</p>
<p>This is line 26 nested</p>
<p>This is line 27 nested</p>
<p>This is line 28 nested</p>
<p>This is line 29 ne");
WriteLiteralTo(__razor_template_writer, @"sted</p>
<p>This is line 30 nested</p>
<p>This is line 31 nested</p>
<p>This is line 32 nested</p>
<p>This is line 33 nested</p>
<p>This is line 34 nested</p>
<p>This is line 35 nested</p>
<p>This is line 36 nested</p>
<p>This is line 37 nested</p>
<p>This is line 38 nested</p>
<p>This is line 39 nested</p>
<p>This is line 40 nested</p>
<p>This is line 41 nested</p>
<p>This is line 42 nested</p>
");
Instrumentation.EndContext();
}
);
Instrumentation.BeginContext(2503, 499, true);
WriteLiteral(@"<p>This is line 1</p>
<p>This is line 2</p>
<p>This is line 3</p>
<p>This is line 4</p>
<p>This is line 5</p>
<p>This is line 6</p>
<p>This is line 7</p>
<p>This is line 8</p>
<p>This is line 9</p>
<p>This is line 10</p>
<p>This is line 11</p>
<p>This is line 12</p>
<p>This is line 13</p>
<p>This is line 14</p>
<p>This is line 15</p>
<p>This is line 16</p>
<p>This is line 17</p>
<p>This is line 18</p>
<p>This is line 19</p>
<p>This is line 20</p>
<p>This is line 21</p>
<br>");
Instrumentation.EndContext();
}
#pragma warning restore 1998
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ public override async Task ExecuteAsync()
{
__tagHelperRunner = __tagHelperRunner ?? new global::Microsoft.AspNet.Razor.Runtime.TagHelperRunner();
Instrumentation.BeginContext(23, 253, true);
WriteLiteral("\r\n<ul [item]=\"items\"></ul>\r\n<ul [(item)]=\"items\"></ul>\r\n<button (click)=\"doSometh" +
"ing()\">Click Me</button>\r\n<button (^click)=\"doSomething()\">Click Me</button>\r\n<t" +
"emplate *something=\"value\">\r\n</template>\r\n<div #local></div>\r\n<div #local=\"value" +
"\"></div>\r\n\r\n");
WriteLiteral("\r\n<ul [item]=\"items\"></ul>\r\n<ul [(item)]=\"items\"></ul>\r\n<button (click)=\"doSomething()\">Click Me</button>\r\n<button (^click)=\"doSomething()\">Click Me</button>\r\n<template *something=\"value\">\r\n</template>\r\n<div #local></div>\r\n<div #local=\"value\"></div>\r\n\r\n");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("ul", global::Microsoft.AspNet.Razor.TagHelpers.TagMode.StartTagAndEndTag, "test", async() => {
}
Expand Down
Loading

0 comments on commit 4db32ec

Please sign in to comment.