Merge branch 'master' of ..\aspnetcore-tooling2\

This commit is contained in:
John Luo 2020-05-27 15:14:25 -07:00
commit 7b56497d5e
25 changed files with 7493 additions and 0 deletions

View File

@ -0,0 +1 @@
[assembly: BenchmarkDotNet.Attributes.AspNetCoreBenchmark]

View File

@ -0,0 +1,58 @@
// 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.IO;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.AspNetCore.Razor.Performance
{
public class CodeGenerationBenchmark
{
public CodeGenerationBenchmark()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null && !File.Exists(Path.Combine(current.FullName, "MSN.cshtml")))
{
current = current.Parent;
}
var root = current;
var fileSystem = RazorProjectFileSystem.Create(root.FullName);
ProjectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, b => RazorExtensions.Register(b)); ;
MSN = fileSystem.GetItem(Path.Combine(root.FullName, "MSN.cshtml"), FileKinds.Legacy);
}
public RazorProjectEngine ProjectEngine { get; }
public RazorProjectItem MSN { get; }
[Benchmark(Description = "Razor Design Time Code Generation of MSN.com")]
public void CodeGeneration_DesignTime_LargeStaticFile()
{
var codeDocument = ProjectEngine.ProcessDesignTime(MSN);
var generated = codeDocument.GetCSharpDocument();
if (generated.Diagnostics.Count != 0)
{
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, generated.Diagnostics));
}
}
[Benchmark(Description = "Razor Runtime Code Generation of MSN.com")]
public void CodeGeneration_Runtime_LargeStaticFile()
{
var codeDocument = ProjectEngine.Process(MSN);
var generated = codeDocument.GetCSharpDocument();
if (generated.Diagnostics.Count != 0)
{
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, generated.Diagnostics));
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<OutputType>Exe</OutputType>
<ServerGarbageCollection>true</ServerGarbageCollection>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" />
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\src\Microsoft.VisualStudio.LanguageServices.Razor\Serialization\*.cs">
<Link>Serialization\%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestServices.cs">
<Link>TestServices\%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestWorkspace.cs">
<Link>TestServices\%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestLanguageServices.cs">
<Link>TestServices\%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\..\test\Microsoft.CodeAnalysis.Razor.Workspaces.Test.Common\TestWorkspaceServices.cs">
<Link>TestServices\%(FileName)%(Extension)</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="$(BenchmarkDotNetPackageVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="$(MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,53 @@
// 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.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.Performance
{
public class BackgroundCodeGenerationBenchmark : ProjectSnapshotManagerBenchmarkBase
{
[IterationSetup]
public void Setup()
{
SnapshotManager = CreateProjectSnapshotManager();
SnapshotManager.ProjectAdded(HostProject);
SnapshotManager.Changed += SnapshotManager_Changed;
}
[IterationCleanup]
public void Cleanup()
{
SnapshotManager.Changed -= SnapshotManager_Changed;
Tasks.Clear();
}
private List<Task> Tasks { get; } = new List<Task>();
private DefaultProjectSnapshotManager SnapshotManager { get; set; }
[Benchmark(Description = "Generates the code for 100 files", OperationsPerInvoke = 100)]
public async Task BackgroundCodeGeneration_Generate100Files()
{
for (var i = 0; i < Documents.Length; i++)
{
SnapshotManager.DocumentAdded(HostProject, Documents[i], TextLoaders[i % 4]);
}
await Task.WhenAll(Tasks);
}
private void SnapshotManager_Changed(object sender, ProjectChangeEventArgs e)
{
// The real work happens here.
var project = SnapshotManager.GetLoadedProject(e.ProjectFilePath);
var document = project.GetDocument(e.DocumentFilePath);
Tasks.Add(document.GetGeneratedOutputAsync());
}
}
}

View File

@ -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 BenchmarkDotNet.Attributes;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
namespace Microsoft.AspNetCore.Razor.Performance
{
public class ProjectLoadBenchmark : ProjectSnapshotManagerBenchmarkBase
{
[IterationSetup]
public void Setup()
{
SnapshotManager = CreateProjectSnapshotManager();
}
private DefaultProjectSnapshotManager SnapshotManager { get; set; }
[Benchmark(Description = "Initializes a project and 100 files", OperationsPerInvoke = 100)]
public void ProjectLoad_AddProjectAnd100Files()
{
SnapshotManager.ProjectAdded(HostProject);
for (var i= 0; i < Documents.Length; i++)
{
SnapshotManager.DocumentAdded(HostProject, Documents[i], TextLoaders[i % 4]);
}
}
}
}

View File

@ -0,0 +1,155 @@
// 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.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Razor.Serialization;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Razor.Performance
{
public class ProjectSnapshotManagerBenchmarkBase
{
public ProjectSnapshotManagerBenchmarkBase()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null && !File.Exists(Path.Combine(current.FullName, "Razor.sln")))
{
current = current.Parent;
}
var root = current;
var projectRoot = Path.Combine(root.FullName, "test", "testapps", "LargeProject");
HostProject = new HostProject(Path.Combine(projectRoot, "LargeProject.csproj"), FallbackRazorConfiguration.MVC_2_1, rootNamespace: null);
TextLoaders = new TextLoader[4];
for (var i = 0; i < 4; i++)
{
var filePath = Path.Combine(projectRoot, "Views", "Home", $"View00{i % 4}.cshtml");
var text = SourceText.From(filePath, encoding: null);
TextLoaders[i] = TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create()));
}
Documents = new HostDocument[100];
for (var i = 0; i < Documents.Length; i++)
{
var filePath = Path.Combine(projectRoot, "Views", "Home", $"View00{i % 4}.cshtml");
Documents[i] = new HostDocument(filePath, $"/Views/Home/View00{i}.cshtml", FileKinds.Legacy);
}
var tagHelpers = Path.Combine(root.FullName, "benchmarks", "Microsoft.AspNetCore.Razor.Performance", "taghelpers.json");
TagHelperResolver = new StaticTagHelperResolver(ReadTagHelpers(tagHelpers));
}
internal HostProject HostProject { get; }
internal HostDocument[] Documents { get; }
internal TextLoader[] TextLoaders { get; }
internal TagHelperResolver TagHelperResolver { get; }
internal DefaultProjectSnapshotManager CreateProjectSnapshotManager()
{
var services = TestServices.Create(
new IWorkspaceService[]
{
TagHelperResolver,
new StaticProjectSnapshotProjectEngineFactory(),
},
new ILanguageService[]
{
});
return new DefaultProjectSnapshotManager(
new TestForegroundDispatcher(),
new TestErrorReporter(),
Array.Empty<ProjectSnapshotChangeTrigger>(),
new AdhocWorkspace(services));
}
private static IReadOnlyList<TagHelperDescriptor> ReadTagHelpers(string filePath)
{
var serializer = new JsonSerializer();
serializer.Converters.Add(new RazorDiagnosticJsonConverter());
serializer.Converters.Add(new TagHelperDescriptorJsonConverter());
using (var reader = new JsonTextReader(File.OpenText(filePath)))
{
return serializer.Deserialize<IReadOnlyList<TagHelperDescriptor>>(reader);
}
}
private class TestForegroundDispatcher : ForegroundDispatcher
{
public override bool IsForegroundThread => true;
public override TaskScheduler ForegroundScheduler => TaskScheduler.Default;
public override TaskScheduler BackgroundScheduler => TaskScheduler.Default;
}
private class TestErrorReporter : ErrorReporter
{
public override void ReportError(Exception exception)
{
}
public override void ReportError(Exception exception, ProjectSnapshot project)
{
}
public override void ReportError(Exception exception, Project workspaceProject)
{
}
}
private class StaticTagHelperResolver : TagHelperResolver
{
private readonly IReadOnlyList<TagHelperDescriptor> _tagHelpers;
public StaticTagHelperResolver(IReadOnlyList<TagHelperDescriptor> tagHelpers)
{
this._tagHelpers = tagHelpers;
}
public override Task<TagHelperResolutionResult> GetTagHelpersAsync(Project project, ProjectSnapshot projectSnapshot, CancellationToken cancellationToken = default)
{
return Task.FromResult(new TagHelperResolutionResult(_tagHelpers, Array.Empty<RazorDiagnostic>()));
}
}
private class StaticProjectSnapshotProjectEngineFactory : ProjectSnapshotProjectEngineFactory
{
public override IProjectEngineFactory FindFactory(ProjectSnapshot project)
{
throw new NotImplementedException();
}
public override IProjectEngineFactory FindSerializableFactory(ProjectSnapshot project)
{
throw new NotImplementedException();
}
public override RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action<RazorProjectEngineBuilder> configure)
{
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
RazorExtensions.Register(b);
});
}
}
}
}

View File

@ -0,0 +1,77 @@
// 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.IO;
using System.Linq;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
namespace Microsoft.AspNetCore.Razor.Performance
{
public class SyntaxTreeGenerationBenchmark
{
public SyntaxTreeGenerationBenchmark()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null && !File.Exists(Path.Combine(current.FullName, "MSN.cshtml")))
{
current = current.Parent;
}
var root = current;
var fileSystem = RazorProjectFileSystem.Create(root.FullName);
ProjectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, b => RazorExtensions.Register(b)); ;
var projectItem = fileSystem.GetItem(Path.Combine(root.FullName, "MSN.cshtml"), FileKinds.Legacy);
MSN = RazorSourceDocument.ReadFrom(projectItem);
var directiveFeature = ProjectEngine.EngineFeatures.OfType<IRazorDirectiveFeature>().FirstOrDefault();
Directives = directiveFeature?.Directives.ToArray() ?? Array.Empty<DirectiveDescriptor>();
}
public RazorProjectEngine ProjectEngine { get; }
public RazorSourceDocument MSN { get; }
public DirectiveDescriptor[] Directives { get; }
[Benchmark(Description = "Razor Design Time Syntax Tree Generation of MSN.com")]
public void SyntaxTreeGeneration_DesignTime_LargeStaticFile()
{
var options = RazorParserOptions.CreateDesignTime(o =>
{
foreach (var directive in Directives)
{
o.Directives.Add(directive);
}
});
var syntaxTree = RazorSyntaxTree.Parse(MSN, options);
if (syntaxTree.Diagnostics.Count != 0)
{
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, syntaxTree.Diagnostics));
}
}
[Benchmark(Description = "Razor Runtime Syntax Tree Generation of MSN.com")]
public void SyntaxTreeGeneration_Runtime_LargeStaticFile()
{
var options = RazorParserOptions.Create(o =>
{
foreach (var directive in Directives)
{
o.Directives.Add(directive);
}
});
var syntaxTree = RazorSyntaxTree.Parse(MSN, options);
if (syntaxTree.Diagnostics.Count != 0)
{
throw new Exception("Error!" + Environment.NewLine + string.Join(Environment.NewLine, syntaxTree.Diagnostics));
}
}
}
}

View File

@ -0,0 +1,55 @@
// 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.IO;
using System.Text;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis.Razor.Serialization;
using Microsoft.VisualStudio.LanguageServices.Razor.Serialization;
using Newtonsoft.Json;
namespace Microsoft.AspNetCore.Razor.Performance
{
public class TagHelperSerializationBenchmark
{
private readonly byte[] _tagHelperBuffer;
public TagHelperSerializationBenchmark()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null && !File.Exists(Path.Combine(current.FullName, "taghelpers.json")))
{
current = current.Parent;
}
var tagHelperFilePath = Path.Combine(current.FullName, "taghelpers.json");
_tagHelperBuffer = File.ReadAllBytes(tagHelperFilePath);
}
[Benchmark(Description = "Razor TagHelper Serialization")]
public void TagHelper_Serialization_RoundTrip()
{
var serializer = new JsonSerializer();
serializer.Converters.Add(new RazorDiagnosticJsonConverter());
serializer.Converters.Add(new TagHelperDescriptorJsonConverter());
// Deserialize from json file.
IReadOnlyList<TagHelperDescriptor> tagHelpers;
using (var stream = new MemoryStream(_tagHelperBuffer))
using (var reader = new JsonTextReader(new StreamReader(stream)))
{
tagHelpers = serializer.Deserialize<IReadOnlyList<TagHelperDescriptor>>(reader);
}
// Serialize back to json.
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 4096))
{
serializer.Serialize(writer, tagHelpers);
}
}
}
}

View File

@ -0,0 +1,11 @@
Compile the solution in Release mode (so binaries are available in release)
To run a specific benchmark add it as parameter.
```
dotnet run -c Release <benchmark_name>
```
If you run without any parameters, you'll be offered the list of all benchmarks and get to choose.
```
dotnet run -c Release
```

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,358 @@
// 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.IO;
using System.Linq;
namespace RazorSyntaxGenerator
{
internal abstract class AbstractFileWriter
{
private readonly TextWriter _writer;
private readonly Tree _tree;
private readonly IDictionary<string, string> _parentMap;
private readonly ILookup<string, string> _childMap;
private readonly IDictionary<string, Node> _nodeMap;
private readonly IDictionary<string, TreeType> _typeMap;
private const int INDENT_SIZE = 4;
private int _indentLevel;
private bool _needIndent = true;
protected AbstractFileWriter(TextWriter writer, Tree tree)
{
_writer = writer;
_tree = tree;
_nodeMap = tree.Types.OfType<Node>().ToDictionary(n => n.Name);
_typeMap = tree.Types.ToDictionary(n => n.Name);
_parentMap = tree.Types.ToDictionary(n => n.Name, n => n.Base);
_parentMap.Add(tree.Root, null);
_childMap = tree.Types.ToLookup(n => n.Base, n => n.Name);
}
protected IDictionary<string, string> ParentMap { get { return _parentMap; } }
protected ILookup<string, string> ChildMap { get { return _childMap; } }
protected Tree Tree { get { return _tree; } }
#region Output helpers
protected void Indent()
{
_indentLevel++;
}
protected void Unindent()
{
if (_indentLevel <= 0)
{
throw new InvalidOperationException("Cannot unindent from base level");
}
_indentLevel--;
}
protected void Write(string msg)
{
WriteIndentIfNeeded();
_writer.Write(msg);
}
protected void Write(string msg, params object[] args)
{
WriteIndentIfNeeded();
_writer.Write(msg, args);
}
protected void WriteLine()
{
WriteLine("");
}
protected void WriteLine(string msg)
{
WriteIndentIfNeeded();
_writer.WriteLine(msg);
_needIndent = true; //need an indent after each line break
}
protected void WriteLine(string msg, params object[] args)
{
WriteIndentIfNeeded();
_writer.WriteLine(msg, args);
_needIndent = true; //need an indent after each line break
}
private void WriteIndentIfNeeded()
{
if (_needIndent)
{
_writer.Write(new string(' ', _indentLevel * INDENT_SIZE));
_needIndent = false;
}
}
protected void OpenBlock()
{
WriteLine("{");
Indent();
}
protected void CloseBlock()
{
Unindent();
WriteLine("}");
}
#endregion Output helpers
#region Node helpers
protected static string OverrideOrNewModifier(Field field)
{
return IsOverride(field) ? "override " : IsNew(field) ? "new " : "";
}
protected static bool CanBeField(Field field)
{
return field.Type != "SyntaxToken" && !IsAnyList(field.Type) && !IsOverride(field) && !IsNew(field);
}
protected static string GetFieldType(Field field, bool green)
{
if (IsAnyList(field.Type))
{
return green
? "GreenNode"
: "SyntaxNode";
}
return field.Type;
}
protected bool IsDerivedOrListOfDerived(string baseType, string derivedType)
{
return IsDerivedType(baseType, derivedType)
|| ((IsNodeList(derivedType) || IsSeparatedNodeList(derivedType))
&& IsDerivedType(baseType, GetElementType(derivedType)));
}
protected static bool IsSeparatedNodeList(string typeName)
{
return typeName.StartsWith("SeparatedSyntaxList<", StringComparison.Ordinal);
}
protected static bool IsNodeList(string typeName)
{
return typeName.StartsWith("SyntaxList<", StringComparison.Ordinal);
}
protected static bool IsAnyNodeList(string typeName)
{
return IsNodeList(typeName) || IsSeparatedNodeList(typeName);
}
protected bool IsNodeOrNodeList(string typeName)
{
return IsNode(typeName) || IsNodeList(typeName) || IsSeparatedNodeList(typeName) || typeName == "SyntaxNodeOrTokenList";
}
protected static string GetElementType(string typeName)
{
if (!typeName.Contains("<"))
return string.Empty;
var iStart = typeName.IndexOf('<');
var iEnd = typeName.IndexOf('>', iStart + 1);
if (iEnd < iStart)
return string.Empty;
var sub = typeName.Substring(iStart + 1, iEnd - iStart - 1);
return sub;
}
protected static bool IsAnyList(string typeName)
{
return IsNodeList(typeName) || IsSeparatedNodeList(typeName) || typeName == "SyntaxNodeOrTokenList";
}
protected bool IsDerivedType(string typeName, string derivedTypeName)
{
if (typeName == derivedTypeName)
return true;
if (derivedTypeName != null && _parentMap.TryGetValue(derivedTypeName, out var baseType))
{
return IsDerivedType(typeName, baseType);
}
return false;
}
protected static bool IsRoot(Node n)
{
return n.Root != null && string.Compare(n.Root, "true", true) == 0;
}
protected bool IsNode(string typeName)
{
return _parentMap.ContainsKey(typeName);
}
protected Node GetNode(string typeName)
=> _nodeMap.TryGetValue(typeName, out var node) ? node : null;
protected TreeType GetTreeType(string typeName)
=> _typeMap.TryGetValue(typeName, out var node) ? node : null;
protected static bool IsOptional(Field f)
{
return f.Optional != null && string.Compare(f.Optional, "true", true) == 0;
}
protected static bool IsOverride(Field f)
{
return f.Override != null && string.Compare(f.Override, "true", true) == 0;
}
protected static bool IsNew(Field f)
{
return f.New != null && string.Compare(f.New, "true", true) == 0;
}
protected static bool HasErrors(Node n)
{
return n.Errors == null || string.Compare(n.Errors, "true", true) == 0;
}
protected static string CamelCase(string name)
{
if (char.IsUpper(name[0]))
{
name = char.ToLowerInvariant(name[0]) + name.Substring(1);
}
return FixKeyword(name);
}
protected static string FixKeyword(string name)
{
if (IsKeyword(name))
{
return "@" + name;
}
return name;
}
protected static string UnderscoreCamelCase(string name)
{
return "_" + CamelCase(name);
}
protected string StripNode(string name)
{
return (_tree.Root.EndsWith("Node", StringComparison.Ordinal)) ? _tree.Root.Substring(0, _tree.Root.Length - 4) : _tree.Root;
}
protected string StripRoot(string name)
{
var root = StripNode(_tree.Root);
if (name.EndsWith(root, StringComparison.Ordinal))
{
return name.Substring(0, name.Length - root.Length);
}
return name;
}
protected static string StripPost(string name, string post)
{
return name.EndsWith(post, StringComparison.Ordinal)
? name.Substring(0, name.Length - post.Length)
: name;
}
protected static bool IsKeyword(string name)
{
switch (name)
{
case "bool":
case "byte":
case "sbyte":
case "short":
case "ushort":
case "int":
case "uint":
case "long":
case "ulong":
case "double":
case "float":
case "decimal":
case "string":
case "char":
case "object":
case "typeof":
case "sizeof":
case "null":
case "true":
case "false":
case "if":
case "else":
case "while":
case "for":
case "foreach":
case "do":
case "switch":
case "case":
case "default":
case "lock":
case "try":
case "throw":
case "catch":
case "finally":
case "goto":
case "break":
case "continue":
case "return":
case "public":
case "private":
case "internal":
case "protected":
case "static":
case "readonly":
case "sealed":
case "const":
case "new":
case "override":
case "abstract":
case "virtual":
case "partial":
case "ref":
case "out":
case "in":
case "where":
case "params":
case "this":
case "base":
case "namespace":
case "using":
case "class":
case "struct":
case "interface":
case "delegate":
case "checked":
case "get":
case "set":
case "add":
case "remove":
case "operator":
case "implicit":
case "explicit":
case "fixed":
case "extern":
case "event":
case "enum":
case "unsafe":
return true;
default:
return false;
}
}
#endregion Node helpers
}
}

View File

@ -0,0 +1,14 @@
// 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.Xml.Serialization;
namespace RazorSyntaxGenerator
{
public class AbstractNode : TreeType
{
[XmlElement(ElementName = "Field", Type = typeof(Field))]
public List<Field> Fields;
}
}

View File

@ -0,0 +1,14 @@
// 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.Xml;
using System.Xml.Serialization;
namespace RazorSyntaxGenerator
{
public class Comment
{
[XmlAnyElement]
public XmlElement[] Body;
}
}

View File

@ -0,0 +1,32 @@
// 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.Xml.Serialization;
namespace RazorSyntaxGenerator
{
public class Field
{
[XmlAttribute]
public string Name;
[XmlAttribute]
public string Type;
[XmlAttribute]
public string Optional;
[XmlAttribute]
public string Override;
[XmlAttribute]
public string New;
[XmlElement(ElementName = "Kind", Type = typeof(Kind))]
public List<Kind> Kinds;
[XmlElement]
public Comment PropertyComment;
}
}

View File

@ -0,0 +1,13 @@
// 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.Xml.Serialization;
namespace RazorSyntaxGenerator
{
public class Kind
{
[XmlAttribute]
public string Name;
}
}

View File

@ -0,0 +1,23 @@
// 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.Xml.Serialization;
namespace RazorSyntaxGenerator
{
public class Node : TreeType
{
[XmlAttribute]
public string Root;
[XmlAttribute]
public string Errors;
[XmlElement(ElementName = "Kind", Type = typeof(Kind))]
public List<Kind> Kinds;
[XmlElement(ElementName = "Field", Type = typeof(Field))]
public List<Field> Fields;
}
}

View File

@ -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 RazorSyntaxGenerator
{
public class PredefinedNode : TreeType
{
}
}

View File

@ -0,0 +1,20 @@
// 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.Xml.Serialization;
namespace RazorSyntaxGenerator
{
[XmlRoot]
public class Tree
{
[XmlAttribute]
public string Root;
[XmlElement(ElementName = "Node", Type = typeof(Node))]
[XmlElement(ElementName = "AbstractNode", Type = typeof(AbstractNode))]
[XmlElement(ElementName = "PredefinedNode", Type = typeof(PredefinedNode))]
public List<TreeType> Types;
}
}

View File

@ -0,0 +1,22 @@
// 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.Xml.Serialization;
namespace RazorSyntaxGenerator
{
public class TreeType
{
[XmlAttribute]
public string Name;
[XmlAttribute]
public string Base;
[XmlElement]
public Comment TypeComment;
[XmlElement]
public Comment FactoryComment;
}
}

View File

@ -0,0 +1,112 @@
// 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.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace RazorSyntaxGenerator
{
public class Program
{
public static int Main(string[] args)
{
if (args.Length < 2 || args.Length > 2)
{
WriteUsage();
return 1;
}
var inputFile = args[0];
if (!File.Exists(inputFile))
{
Console.WriteLine(Directory.GetCurrentDirectory());
Console.WriteLine(inputFile + " not found.");
return 1;
}
var writeSource = true;
var writeSignatures = false;
string outputFile = null;
if (args.Length == 2)
{
if (args[1] == "/sig")
{
writeSignatures = true;
}
else
{
outputFile = args[1];
}
}
var reader = XmlReader.Create(inputFile, new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit });
var serializer = new XmlSerializer(typeof(Tree));
var tree = (Tree)serializer.Deserialize(reader);
if (writeSignatures)
{
SignatureWriter.Write(Console.Out, tree);
}
else
{
if (writeSource)
{
var outputPath = outputFile.Trim('"');
var prefix = Path.GetFileName(inputFile);
var outputMainFile = Path.Combine(outputPath, $"{prefix}.Main.Generated.cs");
var outputInternalFile = Path.Combine(outputPath, $"{prefix}.Internal.Generated.cs");
var outputSyntaxFile = Path.Combine(outputPath, $"{prefix}.Syntax.Generated.cs");
WriteToFile(tree, SourceWriter.WriteMain, outputMainFile);
WriteToFile(tree, SourceWriter.WriteInternal, outputInternalFile);
WriteToFile(tree, SourceWriter.WriteSyntax, outputSyntaxFile);
}
//if (writeTests)
//{
// WriteToFile(tree, TestWriter.Write, outputFile);
//}
}
return 0;
}
private static void WriteUsage()
{
Console.WriteLine("Invalid usage");
Console.WriteLine(typeof(Program).GetTypeInfo().Assembly.ManifestModule.Name + " input-file output-file [/write-test]");
}
private static void WriteToFile(Tree tree, Action<TextWriter, Tree> writeAction, string outputFile)
{
var stringBuilder = new StringBuilder();
var writer = new StringWriter(stringBuilder);
writeAction(writer, tree);
var text = stringBuilder.ToString();
int length;
do
{
length = text.Length;
text = text.Replace("{\r\n\r\n", "{\r\n");
} while (text.Length != length);
try
{
using (var outFile = new StreamWriter(File.Open(outputFile, FileMode.Create), Encoding.UTF8))
{
outFile.Write(text);
}
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Unable to access {0}. Is it checked out?", outputFile);
}
}
}
}

View File

@ -0,0 +1,7 @@
# Razor syntax generator
Syntax generator tool for the Razor syntax tree. This is a modified version of Roslyn's [CSharpSyntaxGenerator](https://github.com/dotnet/roslyn/tree/master/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator). For internal use only.
## Usage
dotnet run `full/path/to/Syntax.xml` `full/path/to/generated/output`

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Generates Razor syntax nodes from xml. For internal use only.</Description>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>dotnet-razorsyntaxgenerator</AssemblyName>
<PackageId>RazorSyntaxGenerator</PackageId>
<OutputType>Exe</OutputType>
<EnableApiCheck>false</EnableApiCheck>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,130 @@
// 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.IO;
using System.Linq;
namespace RazorSyntaxGenerator
{
internal class SignatureWriter
{
private readonly TextWriter _writer;
private readonly Tree _tree;
private readonly Dictionary<string, string> _typeMap;
private SignatureWriter(TextWriter writer, Tree tree)
{
_writer = writer;
_tree = tree;
_typeMap = tree.Types.ToDictionary(n => n.Name, n => n.Base);
_typeMap.Add(tree.Root, null);
}
public static void Write(TextWriter writer, Tree tree)
{
new SignatureWriter(writer, tree).WriteFile();
}
private void WriteFile()
{
_writer.WriteLine("using System;");
_writer.WriteLine("using System.Collections;");
_writer.WriteLine("using System.Collections.Generic;");
_writer.WriteLine("using System.Linq;");
_writer.WriteLine("using System.Threading;");
_writer.WriteLine();
_writer.WriteLine("namespace Microsoft.AspNetCore.Razor.Language.Syntax");
_writer.WriteLine("{");
this.WriteTypes();
_writer.WriteLine("}");
}
private void WriteTypes()
{
var nodes = _tree.Types.Where(n => !(n is PredefinedNode)).ToList();
for (int i = 0, n = nodes.Count; i < n; i++)
{
var node = nodes[i];
_writer.WriteLine();
WriteType(node);
}
}
private void WriteType(TreeType node)
{
if (node is AbstractNode abstractNode)
{
_writer.WriteLine(" public abstract partial class {0} : {1}", node.Name, node.Base);
_writer.WriteLine(" {");
for (int i = 0, n = abstractNode.Fields.Count; i < n; i++)
{
var field = abstractNode.Fields[i];
if (IsNodeOrNodeList(field.Type))
{
_writer.WriteLine(" public abstract {0}{1} {2} {{ get; }}", "", field.Type, field.Name);
}
}
_writer.WriteLine(" }");
}
else if (node is Node nd)
{
_writer.WriteLine(" public partial class {0} : {1}", node.Name, node.Base);
_writer.WriteLine(" {");
WriteKinds(nd.Kinds);
var valueFields = nd.Fields.Where(n => !IsNodeOrNodeList(n.Type)).ToList();
var nodeFields = nd.Fields.Where(n => IsNodeOrNodeList(n.Type)).ToList();
for (int i = 0, n = nodeFields.Count; i < n; i++)
{
var field = nodeFields[i];
_writer.WriteLine(" public {0}{1}{2} {3} {{ get; }}", "", "", field.Type, field.Name);
}
for (int i = 0, n = valueFields.Count; i < n; i++)
{
var field = valueFields[i];
_writer.WriteLine(" public {0}{1}{2} {3} {{ get; }}", "", "", field.Type, field.Name);
}
_writer.WriteLine(" }");
}
}
private void WriteKinds(List<Kind> kinds)
{
if (kinds.Count > 1)
{
foreach (var kind in kinds)
{
_writer.WriteLine(" // {0}", kind.Name);
}
}
}
private bool IsSeparatedNodeList(string typeName)
{
return typeName.StartsWith("SeparatedSyntaxList<", StringComparison.Ordinal);
}
private bool IsNodeList(string typeName)
{
return typeName.StartsWith("SyntaxList<", StringComparison.Ordinal);
}
public bool IsNodeOrNodeList(string typeName)
{
return IsNode(typeName) || IsNodeList(typeName) || IsSeparatedNodeList(typeName);
}
private bool IsNode(string typeName)
{
return _typeMap.ContainsKey(typeName);
}
}
}

File diff suppressed because it is too large Load Diff