Skip to content

Commit 576731e

Browse files
committed
Adding contract interfaces for specifying runtime compilation exceptions.
Fixes #12
1 parent 44dadb1 commit 576731e

File tree

12 files changed

+822
-61
lines changed

12 files changed

+822
-61
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using Microsoft.Framework.Runtime;
6+
7+
namespace Microsoft.AspNet.Diagnostics
8+
{
9+
/// <summary>
10+
/// Specifies the contract for an exception representing compilation failure.
11+
/// </summary>
12+
[AssemblyNeutral]
13+
public interface ICompilationException
14+
{
15+
/// <summary>
16+
/// Gets a sequence of <see cref="ICompilationFailure"/> with compilation failures.
17+
/// </summary>
18+
IEnumerable<ICompilationFailure> CompilationFailures { get; }
19+
}
20+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using Microsoft.Framework.Runtime;
6+
7+
namespace Microsoft.AspNet.Diagnostics
8+
{
9+
/// <summary>
10+
/// Specifies the contract for a file that fails compilation.
11+
/// </summary>
12+
[AssemblyNeutral]
13+
public interface ICompilationFailure
14+
{
15+
/// <summary>
16+
/// Path of the file that produced the compilation exception.
17+
/// </summary>
18+
string SourceFilePath { get; }
19+
20+
/// <summary>
21+
/// Contents of the file.
22+
/// </summary>
23+
string SourceFileContent { get; }
24+
25+
/// <summary>
26+
/// Contents being compiled.
27+
/// </summary>
28+
/// <remarks>
29+
/// For templated files, the <see cref="SourceFileContent"/> represents the original content and
30+
/// <see cref="CompiledContent"/> represents the transformed content. This property can be null if
31+
/// the exception is encountered during transformation.
32+
/// </remarks>
33+
string CompiledContent { get; }
34+
35+
/// <summary>
36+
/// Gets a sequence of <see cref="ICompilationMessage"/> produced as a result of compilation.
37+
/// </summary>
38+
IEnumerable<ICompilationMessage> Messages { get; }
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.Framework.Runtime;
5+
6+
namespace Microsoft.AspNet.Diagnostics
7+
{
8+
/// <summary>
9+
/// Specifies the contract for diagnostic messages produced as result of compiling an instance
10+
/// of <see cref="ICompilationFailure"/>.
11+
/// </summary>
12+
[AssemblyNeutral]
13+
public interface ICompilationMessage
14+
{
15+
/// <summary>
16+
/// Gets the error message.
17+
/// </summary>
18+
string Message { get; }
19+
20+
/// <summary>
21+
/// Gets the zero-based line index for the start of the compilation error.
22+
/// </summary>
23+
int StartLine { get; }
24+
25+
/// <summary>
26+
/// Gets the zero-based column index for the start of the compilation error.
27+
/// </summary>
28+
int StartColumn { get; }
29+
30+
/// <summary>
31+
/// Gets the zero-based line index for the end of the compilation error.
32+
/// </summary>
33+
int EndLine { get; }
34+
35+
/// <summary>
36+
/// Gets the zero-based column index for the end of the compilation error.
37+
/// </summary>
38+
int EndColumn { get; }
39+
}
40+
}

src/Microsoft.AspNet.Diagnostics/ErrorPageMiddleware.cs

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,70 @@ public async Task Invoke(HttpContext context)
7676
}
7777

7878
// Assumes the response headers have not been sent. If they have, still attempt to write to the body.
79-
private async Task DisplayException(HttpContext context, Exception ex)
79+
private Task DisplayException(HttpContext context, Exception ex)
80+
{
81+
var compilationException = ex as ICompilationException;
82+
if (compilationException != null)
83+
{
84+
return DisplayCompilationException(context, ex, compilationException);
85+
}
86+
87+
return DisplayRuntimeException(context, ex);
88+
}
89+
90+
private Task DisplayCompilationException(HttpContext context,
91+
Exception ex,
92+
ICompilationException compilationException)
93+
{
94+
var stackFrames = new List<StackFrame>();
95+
var model = new CompilationErrorPageModel()
96+
{
97+
Options = _options,
98+
ErrorDetails = new ErrorDetails
99+
{
100+
Error = ex,
101+
StackFrames = stackFrames
102+
}
103+
};
104+
105+
// For view compilation, the most common case is to stop at the first failing file compiled as part of
106+
// rendering a view. Consequently we'll limit ourselves to displaying errors from the first failure.
107+
var failedCompilationFile = compilationException.CompilationFailures.FirstOrDefault();
108+
if (failedCompilationFile != null)
109+
{
110+
var fileContent = failedCompilationFile.SourceFileContent
111+
.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
112+
113+
foreach (var item in failedCompilationFile.Messages)
114+
{
115+
// Convert 0-based line indexes to 1-based index that the StackFrame expects
116+
var lineIndex = item.StartLine + 1;
117+
var frame = new StackFrame
118+
{
119+
File = failedCompilationFile.SourceFilePath,
120+
Line = lineIndex,
121+
Function = string.Empty
122+
};
123+
124+
if (_options.ShowSourceCode)
125+
{
126+
ReadFrameContent(frame, fileContent, lineIndex, item.EndLine + 1);
127+
frame.ErrorDetails = item.Message;
128+
}
129+
130+
stackFrames.Add(frame);
131+
}
132+
}
133+
134+
var errorPage = new CompilationErrorPage
135+
{
136+
Model = model
137+
};
138+
139+
return errorPage.ExecuteAsync(context);
140+
}
141+
142+
private Task DisplayRuntimeException(HttpContext context, Exception ex)
80143
{
81144
var request = context.Request;
82145

@@ -107,7 +170,7 @@ private async Task DisplayException(HttpContext context, Exception ex)
107170
}*/
108171

109172
var errorPage = new ErrorPage(model);
110-
await errorPage.ExecuteAsync(context);
173+
return errorPage.ExecuteAsync(context);
111174
}
112175

113176
private IEnumerable<ErrorDetails> GetErrorDetails(Exception ex, bool showSource)
@@ -160,14 +223,22 @@ private StackFrame LoadFrame(string function, string file, int lineNumber, bool
160223
if (showSource && File.Exists(file))
161224
{
162225
IEnumerable<string> code = File.ReadLines(file);
163-
frame.PreContextLine = Math.Max(lineNumber - _options.SourceCodeLineCount, 1);
164-
frame.PreContextCode = code.Skip(frame.PreContextLine - 1).Take(lineNumber - frame.PreContextLine).ToArray();
165-
frame.ContextCode = code.Skip(lineNumber - 1).FirstOrDefault();
166-
frame.PostContextCode = code.Skip(lineNumber).Take(_options.SourceCodeLineCount).ToArray();
226+
ReadFrameContent(frame, code, lineNumber, lineNumber);
167227
}
168228
return frame;
169229
}
170230

231+
private void ReadFrameContent(StackFrame frame,
232+
IEnumerable<string> code,
233+
int startLineNumber,
234+
int endLineNumber)
235+
{
236+
frame.PreContextLine = Math.Max(startLineNumber - _options.SourceCodeLineCount, 1);
237+
frame.PreContextCode = code.Skip(frame.PreContextLine - 1).Take(startLineNumber - frame.PreContextLine).ToArray();
238+
frame.ContextCode = code.Skip(startLineNumber - 1).Take(1 + Math.Max(0, endLineNumber - startLineNumber));
239+
frame.PostContextCode = code.Skip(startLineNumber).Take(_options.SourceCodeLineCount).ToArray();
240+
}
241+
171242
internal class Chunk
172243
{
173244
public string Text { get; set; }

src/Microsoft.AspNet.Diagnostics/Properties/Resources.Designer.cs

Lines changed: 18 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.AspNet.Diagnostics/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,7 @@
230230
<data name="WelcomeTitle" xml:space="preserve">
231231
<value>Your ASP.NET vNext application has been successfully started.</value>
232232
</data>
233+
<data name="ErrorPageHtml_CompilationException" xml:space="preserve">
234+
<value>An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.</value>
235+
</data>
233236
</root>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
6+
namespace Microsoft.AspNet.Diagnostics.Views
7+
{
8+
/// <summary>
9+
/// Holds data to be displayed on the compilation error page.
10+
/// </summary>
11+
public class CompilationErrorPageModel
12+
{
13+
/// <summary>
14+
/// Options for what output to display.
15+
/// </summary>
16+
public ErrorPageOptions Options { get; set; }
17+
18+
/// <summary>
19+
/// Detailed information about each parse or compilation error.
20+
/// </summary>
21+
public ErrorDetails ErrorDetails { get; set; }
22+
}
23+
}

0 commit comments

Comments
 (0)