Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Microsoft.Extensions.DependencyInjection for the compiler #100

Merged
merged 5 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/Compiling/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
using System.Xml.Linq;

using Azure.ApiManagement.PolicyToolkit.Compiling;
using Azure.ApiManagement.PolicyToolkit.IoC;
using Azure.ApiManagement.PolicyToolkit.Serialization;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

var config = new ConfigurationBuilder()
.AddCommandLine(args)
Expand All @@ -22,6 +24,12 @@
var files = Directory.GetFiles(options.SourceFolder, "*.cs", SearchOption.AllDirectories)
.Where(p => !Regex.IsMatch(p, @".*[\\/](obj|bin)[\\/].*"));

ServiceCollection serviceCollection = new();
using ServiceProvider serviceProvider = serviceCollection
.SetupCompiler()
.BuildServiceProvider();
CSharpPolicyCompiler compiler = serviceProvider.GetRequiredService<CSharpPolicyCompiler>();

int numberOfErrors = 0;

foreach (var file in files)
Expand All @@ -36,7 +44,7 @@
.Where(c => c.AttributeLists.ContainsAttributeOfType("Document"));
foreach (var document in documents)
{
var result = new CSharpPolicyCompiler(document).Compile();
ICompilationResult result = compiler.Compile(document);

var formatter = new DiagnosticFormatter();
numberOfErrors += result.Diagnostics.Count;
Expand Down Expand Up @@ -70,11 +78,12 @@
var targetFolder = Path.Combine(options.OutputFolder, fileRelativePath);
var targetFile = Path.Combine(targetFolder, policyFileName);
var directoryPath = Path.GetDirectoryName(targetFile);

if (directoryPath is not null && !Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}

File.WriteAllText(targetFile, xml);
Console.Out.WriteLine($"File '{targetFile}' created");
}
Expand Down
41 changes: 7 additions & 34 deletions src/Core/Compiling/CSharpPolicyCompiler.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Reflection;
using System.Xml.Linq;

using Azure.ApiManagement.PolicyToolkit.Authoring;
using Azure.ApiManagement.PolicyToolkit.Compiling.Diagnostics;
using Azure.ApiManagement.PolicyToolkit.Compiling.Policy;
using Azure.ApiManagement.PolicyToolkit.Compiling.Syntax;

using Microsoft.CodeAnalysis;
Expand All @@ -16,44 +14,19 @@ namespace Azure.ApiManagement.PolicyToolkit.Compiling;

public class CSharpPolicyCompiler
{
private ClassDeclarationSyntax _document;
private readonly Lazy<BlockCompiler> _blockCompiler;

private BlockCompiler _blockCompiler;

public CSharpPolicyCompiler(ClassDeclarationSyntax document)
public CSharpPolicyCompiler(Lazy<BlockCompiler> blockCompiler)
{
_document = document;
var handlers = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type =>
type is
{
IsClass: true,
IsAbstract: false,
IsPublic: true,
Namespace: "Azure.ApiManagement.PolicyToolkit.Compiling.Policy"
}
&& typeof(IMethodPolicyHandler).IsAssignableFrom(type))
.Select(t => Activator.CreateInstance(t) as IMethodPolicyHandler)
.Where(h => h is not null)!
.ToArray<IMethodPolicyHandler>();
var invStatement = new ExpressionStatementCompiler(handlers);
var loc = new LocalDeclarationStatementCompiler([
new AuthenticationManageIdentityReturnValueCompiler()
]);
_blockCompiler = new([
invStatement,
loc
]);
_blockCompiler.AddCompiler(new IfStatementCompiler(_blockCompiler));
_blockCompiler = blockCompiler;
}

public ICompilationResult Compile()
public ICompilationResult Compile(ClassDeclarationSyntax document)
{
var methods = _document.DescendantNodes()
var methods = document.DescendantNodes()
.OfType<MethodDeclarationSyntax>();
var policyDocument = new XElement("policies");
var context = new CompilationContext(_document, policyDocument);
CompilationContext context = new(document, policyDocument);

foreach (var method in methods)
{
Expand Down Expand Up @@ -92,7 +65,7 @@ private void CompileSection(ICompilationContext context, string section, MethodD

var sectionElement = new XElement(section);
var sectionContext = new SubCompilationContext(context, sectionElement);
_blockCompiler.Compile(sectionContext, method.Body);
_blockCompiler.Value.Compile(sectionContext, method.Body);
context.AddPolicy(sectionElement);
}
}
12 changes: 5 additions & 7 deletions src/Core/Compiling/Syntax/BlockCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ namespace Azure.ApiManagement.PolicyToolkit.Compiling.Syntax;

public class BlockCompiler : ISyntaxCompiler
{
private IDictionary<SyntaxKind, ISyntaxCompiler> _compilers;
private readonly Dictionary<SyntaxKind, ISyntaxCompiler> _compilers = new();

public BlockCompiler(IEnumerable<ISyntaxCompiler> compilers)
{
_compilers = compilers.ToDictionary(c => c.Syntax);
}

public void AddCompiler(ISyntaxCompiler compiler)
{
_compilers.Add(compiler.Syntax, compiler);
foreach (ISyntaxCompiler compiler in compilers)
{
_compilers.Add(compiler.Syntax, compiler);
}
}

public SyntaxKind Syntax => SyntaxKind.Block;
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Compiling/Syntax/ExpressionStatementCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Azure.ApiManagement.PolicyToolkit.Compiling.Syntax;

public class ExpressionStatementCompiler : ISyntaxCompiler
{
private IReadOnlyDictionary<string, IMethodPolicyHandler> _handlers;
private readonly IReadOnlyDictionary<string, IMethodPolicyHandler> _handlers;

public ExpressionStatementCompiler(IEnumerable<IMethodPolicyHandler> handlers)
{
Expand Down
8 changes: 4 additions & 4 deletions src/Core/Compiling/Syntax/IfStatementCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ namespace Azure.ApiManagement.PolicyToolkit.Compiling.Syntax;

public class IfStatementCompiler : ISyntaxCompiler
{
ISyntaxCompiler _blockCompiler;
private readonly Lazy<BlockCompiler> _blockCompiler;

public IfStatementCompiler(ISyntaxCompiler blockCompiler)
public IfStatementCompiler(Lazy<BlockCompiler> blockCompiler)
{
this._blockCompiler = blockCompiler;
}
Expand Down Expand Up @@ -60,7 +60,7 @@ public void Compile(ICompilationContext context, SyntaxNode node)

var section = new XElement("when");
var innerContext = new SubCompilationContext(context, section);
_blockCompiler.Compile(innerContext, block);
_blockCompiler.Value.Compile(innerContext, block);
section.Add(new XAttribute("condition", CompilerUtils.FindCode(condition, context)));
choose.Add(section);

Expand All @@ -73,7 +73,7 @@ public void Compile(ICompilationContext context, SyntaxNode node)
var innerContext = new SubCompilationContext(context, section);
if (currentIf.Else.Statement is BlockSyntax block)
{
_blockCompiler.Compile(innerContext, block);
_blockCompiler.Value.Compile(innerContext, block);
choose.Add(section);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

public class LocalDeclarationStatementCompiler : ISyntaxCompiler
{
private IReadOnlyDictionary<string, IReturnValueMethodPolicyHandler> _handlers;
private readonly IReadOnlyDictionary<string, IReturnValueMethodPolicyHandler> _handlers;

public LocalDeclarationStatementCompiler(IEnumerable<IReturnValueMethodPolicyHandler> handlers)
{
Expand All @@ -32,9 +32,9 @@
var invocation = variable.Initializer?.Value as InvocationExpressionSyntax;
var memberAccess = invocation?.Expression as MemberAccessExpressionSyntax;
var methodName = memberAccess?.Name.ToString();
if (_handlers.TryGetValue(methodName, out var handler))

Check warning on line 35 in src/Core/Compiling/Syntax/LocalDeclarationStatementCompiler.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'key' in 'bool IReadOnlyDictionary<string, IReturnValueMethodPolicyHandler>.TryGetValue(string key, out IReturnValueMethodPolicyHandler value)'.
{
handler.Handle(context, invocation, variable.Identifier.ValueText);

Check warning on line 37 in src/Core/Compiling/Syntax/LocalDeclarationStatementCompiler.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'node' in 'void IReturnValueMethodPolicyHandler.Handle(ICompilationContext context, InvocationExpressionSyntax node, string variableName)'.
}
else
{
Expand Down
1 change: 1 addition & 0 deletions src/Core/Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0"/>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.2"/>
</ItemGroup>

<ItemGroup>
Expand Down
54 changes: 54 additions & 0 deletions src/Core/IoC/CompilerModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Reflection;

using Azure.ApiManagement.PolicyToolkit.Compiling;
using Azure.ApiManagement.PolicyToolkit.Compiling.Syntax;

using Microsoft.Extensions.DependencyInjection;

namespace Azure.ApiManagement.PolicyToolkit.IoC;

public static class CompilerModule
{
public static IServiceCollection SetupCompiler(this IServiceCollection services)
{
return services
.AddLazyResolution()
.AddSingleton<CSharpPolicyCompiler>()
.AddMethodPolicyHandlers()
.AddSyntaxCompilers();
}

public static IServiceCollection AddMethodPolicyHandlers(this IServiceCollection services)
{
IEnumerable<Type>? policyHandlerTypes = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type =>
type is
{
IsClass: true,
IsAbstract: false,
IsPublic: true,
Namespace: "Azure.ApiManagement.PolicyToolkit.Compiling.Policy"
}
&& typeof(IMethodPolicyHandler).IsAssignableFrom(type));

foreach (Type handlerType in policyHandlerTypes)
{
services.AddSingleton(typeof(IMethodPolicyHandler), handlerType);
}

return services;
}

public static IServiceCollection AddSyntaxCompilers(this IServiceCollection services)
{
return services
.AddSingleton<BlockCompiler>()
.AddSingleton<ISyntaxCompiler, IfStatementCompiler>()
.AddSingleton<ISyntaxCompiler, LocalDeclarationStatementCompiler>()
.AddSingleton<ISyntaxCompiler, ExpressionStatementCompiler>();
}
}
17 changes: 17 additions & 0 deletions src/Core/IoC/LazilyResolutionModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Extensions.DependencyInjection;

namespace Azure.ApiManagement.PolicyToolkit.IoC;

public static class LazilyResolutionModule
{
public static IServiceCollection AddLazyResolution(this IServiceCollection services)
{
return services.AddTransient(typeof(Lazy<>), typeof(LazyRequired<>));
}

private class LazyRequired<T>(IServiceProvider serviceProvider) : Lazy<T>(serviceProvider.GetRequiredService<T>)
where T : notnull;
}
47 changes: 47 additions & 0 deletions test/Test.Core/CompilerTestInitialize.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.ApiManagement.PolicyToolkit.Compiling;
using Azure.ApiManagement.PolicyToolkit.IoC;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Extensions.DependencyInjection;

namespace Azure.ApiManagement.PolicyToolkit.Tests.Extensions;

[TestClass]
public static class CompilerTestInitialize
{
private static ServiceProvider s_serviceProvider;
private static CSharpPolicyCompiler s_compiler;

[AssemblyInitialize]
public static void CompilerInitialize(TestContext testContext)
{
ServiceCollection serviceCollection = new();
s_serviceProvider = serviceCollection
.SetupCompiler()
.BuildServiceProvider();
s_compiler = s_serviceProvider.GetRequiredService<CSharpPolicyCompiler>();
}

[AssemblyCleanup]
public static void CompilerCleanup()
{
s_serviceProvider.Dispose();
}

public static ICompilationResult CompileDocument(this string document)
{
SyntaxTree code = CSharpSyntaxTree.ParseText(document);
ClassDeclarationSyntax policy = code
.GetRoot()
.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.First(c => c.AttributeLists.ContainsAttributeOfType("Document"));

return s_compiler.Compile(policy);
}
}
24 changes: 0 additions & 24 deletions test/Test.Core/StringExtensions.cs

This file was deleted.

Loading
Loading