From ad07036020b5df8bd9173dc0ac0096912f0d7553 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Wed, 6 Jun 2018 08:45:57 -0700 Subject: [PATCH] Use checksum for classname if filepath is null --- .../MvcViewDocumentClassifierPass.cs | 32 ++++++++++++++++++- .../MvcViewDocumentClassifierPass.cs | 32 ++++++++++++++++++- .../RazorPageDocumentClassifierPass.cs | 31 +++++++++++++++++- .../MvcViewDocumentClassifierPassTest.cs | 25 +++++++++++++++ .../RazorPageDocumentClassifierPassTest.cs | 25 +++++++++++++++ .../MvcViewDocumentClassifierPassTest.cs | 25 +++++++++++++++ 6 files changed, 167 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/MvcViewDocumentClassifierPass.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/MvcViewDocumentClassifierPass.cs index 45cc3c1cb9..b4a5c930f6 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/MvcViewDocumentClassifierPass.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/MvcViewDocumentClassifierPass.cs @@ -1,6 +1,8 @@ // 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.Text; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Intermediate; @@ -25,7 +27,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X @namespace.Content = "AspNetCore"; var filePath = codeDocument.Source.RelativePath ?? codeDocument.Source.FilePath; - @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath); + if (string.IsNullOrEmpty(filePath)) + { + // It's possible for a Razor document to not have a file path. + // Eg. When we try to generate code for an in memory document like default imports. + var checksum = BytesToString(codeDocument.Source.GetChecksum()); + @class.ClassName = $"AspNetCore_{checksum}"; + } + else + { + @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath); + } + @class.BaseType = "global::Microsoft.AspNetCore.Mvc.Razor.RazorPage"; @class.Modifiers.Clear(); @class.Modifiers.Add("public"); @@ -37,5 +50,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X method.Modifiers.Add("override"); method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}"; } + + private static string BytesToString(byte[] bytes) + { + if (bytes == null) + { + throw new ArgumentNullException(nameof(bytes)); + } + + var result = new StringBuilder(bytes.Length); + for (var i = 0; i < bytes.Length; i++) + { + // The x2 format means lowercase hex, where each byte is a 2-character string. + result.Append(bytes[i].ToString("x2")); + } + + return result.ToString(); + } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcViewDocumentClassifierPass.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcViewDocumentClassifierPass.cs index 2378f23591..57e9ad23a2 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcViewDocumentClassifierPass.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/MvcViewDocumentClassifierPass.cs @@ -1,6 +1,8 @@ // 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.Text; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Intermediate; @@ -25,7 +27,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions @namespace.Content = "AspNetCore"; var filePath = codeDocument.Source.RelativePath ?? codeDocument.Source.FilePath; - @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath); + if (string.IsNullOrEmpty(filePath)) + { + // It's possible for a Razor document to not have a file path. + // Eg. When we try to generate code for an in memory document like default imports. + var checksum = BytesToString(codeDocument.Source.GetChecksum()); + @class.ClassName = $"AspNetCore_{checksum}"; + } + else + { + @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath); + } + @class.BaseType = "global::Microsoft.AspNetCore.Mvc.Razor.RazorPage"; @class.Modifiers.Clear(); @class.Modifiers.Add("public"); @@ -37,5 +50,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions method.Modifiers.Add("override"); method.ReturnType = $"global::{typeof(System.Threading.Tasks.Task).FullName}"; } + + private static string BytesToString(byte[] bytes) + { + if (bytes == null) + { + throw new ArgumentNullException(nameof(bytes)); + } + + var result = new StringBuilder(bytes.Length); + for (var i = 0; i < bytes.Length; i++) + { + // The x2 format means lowercase hex, where each byte is a 2-character string. + result.Append(bytes[i].ToString("x2")); + } + + return result.ToString(); + } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs index daf8e0e606..9af7873874 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/RazorPageDocumentClassifierPass.cs @@ -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.Diagnostics; +using System.Text; using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language.Extensions; using Microsoft.AspNetCore.Razor.Language.Intermediate; @@ -52,7 +54,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions @class.BaseType = "global::Microsoft.AspNetCore.Mvc.RazorPages.Page"; var filePath = codeDocument.Source.RelativePath ?? codeDocument.Source.FilePath; - @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath); + if (string.IsNullOrEmpty(filePath)) + { + // It's possible for a Razor document to not have a file path. + // Eg. When we try to generate code for an in memory document like default imports. + var checksum = BytesToString(codeDocument.Source.GetChecksum()); + @class.ClassName = $"AspNetCore_{checksum}"; + } + else + { + @class.ClassName = CSharpIdentifier.GetClassNameFromPath(filePath); + } @class.Modifiers.Clear(); @class.Modifiers.Add("public"); @@ -131,5 +143,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions options.ParseLeadingDirectives = true; } } + + private static string BytesToString(byte[] bytes) + { + if (bytes == null) + { + throw new ArgumentNullException(nameof(bytes)); + } + + var result = new StringBuilder(bytes.Length); + for (var i = 0; i < bytes.Length; i++) + { + // The x2 format means lowercase hex, where each byte is a 2-character string. + result.Append(bytes[i].ToString("x2")); + } + + return result.ToString(); + } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/MvcViewDocumentClassifierPassTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/MvcViewDocumentClassifierPassTest.cs index 212e379554..d7c0ae6854 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/MvcViewDocumentClassifierPassTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/MvcViewDocumentClassifierPassTest.cs @@ -97,6 +97,31 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions Assert.Equal("Test", visitor.Class.ClassName); } + [Fact] + public void MvcViewDocumentClassifierPass_NullFilePath_SetsClass() + { + // Arrange + var properties = new RazorSourceDocumentProperties(filePath: null, relativePath: null); + var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", properties)); + + var projectEngine = CreateProjectEngine(); + var irDocument = CreateIRDocument(projectEngine, codeDocument); + var pass = new MvcViewDocumentClassifierPass + { + Engine = projectEngine.Engine + }; + + // Act + pass.Execute(codeDocument, irDocument); + var visitor = new Visitor(); + visitor.Visit(irDocument); + + // Assert + Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", visitor.Class.BaseType); + Assert.Equal(new[] { "public" }, visitor.Class.Modifiers); + Assert.Equal("AspNetCore_d9f877a857a7e9928eac04d09a59f25967624155", visitor.Class.ClassName); + } + [Theory] [InlineData("/Views/Home/Index.cshtml", "_Views_Home_Index")] [InlineData("/Areas/MyArea/Views/Home/About.cshtml", "_Areas_MyArea_Views_Home_About")] diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/RazorPageDocumentClassifierPassTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/RazorPageDocumentClassifierPassTest.cs index 69b6473cfd..a0cdbd7f80 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/RazorPageDocumentClassifierPassTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Test/RazorPageDocumentClassifierPassTest.cs @@ -212,6 +212,31 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions Assert.Equal("Test", visitor.Class.ClassName); } + [Fact] + public void RazorPageDocumentClassifierPass_NullFilePath_SetsClass() + { + // Arrange + var properties = new RazorSourceDocumentProperties(filePath: null, relativePath: null); + var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties)); + + var engine = CreateEngine(); + var irDocument = CreateIRDocument(engine, codeDocument); + var pass = new RazorPageDocumentClassifierPass + { + Engine = engine + }; + + // Act + pass.Execute(codeDocument, irDocument); + var visitor = new Visitor(); + visitor.Visit(irDocument); + + // Assert + Assert.Equal("global::Microsoft.AspNetCore.Mvc.RazorPages.Page", visitor.Class.BaseType); + Assert.Equal(new[] { "public" }, visitor.Class.Modifiers); + Assert.Equal("AspNetCore_74fbaab062bb228ed1ab09c5ff8d6ed2417320e2", visitor.Class.ClassName); + } + [Theory] [InlineData("/Views/Home/Index.cshtml", "_Views_Home_Index")] [InlineData("/Areas/MyArea/Views/Home/About.cshtml", "_Areas_MyArea_Views_Home_About")] diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/MvcViewDocumentClassifierPassTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/MvcViewDocumentClassifierPassTest.cs index be4b1ed4c8..663aff4518 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/MvcViewDocumentClassifierPassTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X.Test/MvcViewDocumentClassifierPassTest.cs @@ -97,6 +97,31 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X Assert.Equal("Test", visitor.Class.ClassName); } + [Fact] + public void MvcViewDocumentClassifierPass_NullFilePath_SetsClass() + { + // Arrange + var properties = new RazorSourceDocumentProperties(filePath: null, relativePath: null); + var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("some-content", properties)); + + var projectEngine = CreateProjectEngine(); + var irDocument = CreateIRDocument(projectEngine, codeDocument); + var pass = new MvcViewDocumentClassifierPass + { + Engine = projectEngine.Engine + }; + + // Act + pass.Execute(codeDocument, irDocument); + var visitor = new Visitor(); + visitor.Visit(irDocument); + + // Assert + Assert.Equal("global::Microsoft.AspNetCore.Mvc.Razor.RazorPage", visitor.Class.BaseType); + Assert.Equal(new[] { "public" }, visitor.Class.Modifiers); + Assert.Equal("AspNetCore_d9f877a857a7e9928eac04d09a59f25967624155", visitor.Class.ClassName); + } + [Theory] [InlineData("/Views/Home/Index.cshtml", "_Views_Home_Index")] [InlineData("/Areas/MyArea/Views/Home/About.cshtml", "_Areas_MyArea_Views_Home_About")]