├── .gitignore ├── .gitmodules ├── LICENSE ├── PERF ├── DuckGenerator.cs ├── GeneratedDuck.cs ├── GeneratedProxy.cs ├── IInterface.cs ├── Program.cs ├── ProxyGen.Perf.csproj ├── ProxyGenerator.cs ├── docfx.json └── index.md ├── ProxyGen.sln ├── README.MD ├── SRC ├── Private │ ├── Attributes │ │ └── SupportsSourceGenerationAttributeBase.cs │ ├── Comparers │ │ ├── ComparerBase.cs │ │ ├── MetadataReferenceComparer.cs │ │ ├── Reflection │ │ │ ├── IAssemblyInfoComparer.cs │ │ │ └── ITypeInfoComparer.cs │ │ └── TypeComparer.cs │ ├── Compile.cs │ ├── Config │ │ ├── AnalyzerConfig.cs │ │ ├── ConfigBase.cs │ │ └── RuntimeConfig.cs │ ├── CurrentMember.cs │ ├── Enums │ │ ├── LogLevel.cs │ │ ├── OutputType.cs │ │ └── Reflection │ │ │ ├── AccessModifiers.cs │ │ │ ├── ParameterKind.cs │ │ │ ├── RefType.cs │ │ │ └── TypeInfoFlags.cs │ ├── Exceptions │ │ └── InvalidSymbolException.cs │ ├── Extensions │ │ ├── CodeAnalysisExtensions.cs │ │ ├── EnumExtensions.cs │ │ ├── ExceptionExtensions.cs │ │ ├── HashExtensions.cs │ │ ├── IEnumerableExtensions.cs │ │ ├── Metadata │ │ │ ├── MemberInfoExtensions.cs │ │ │ ├── MethodInfoExtensions.cs │ │ │ ├── ParameterInfoExtensions.cs │ │ │ ├── PropertyInfoExtensions.cs │ │ │ └── TypeExtensions.cs │ │ ├── NumberExtensions.cs │ │ ├── Reflection │ │ │ ├── IEventInfoExtensions.cs │ │ │ ├── IGenericConstraintExtensions.cs │ │ │ ├── IMethodInfoExtensions.cs │ │ │ ├── IParameterInfoExtensions.cs │ │ │ ├── IPropertyInfoExtensions.cs │ │ │ └── ITypeInfoExtensions.cs │ │ ├── StreamExtensions.cs │ │ ├── StreamWriterExtensions.cs │ │ └── Symbol │ │ │ ├── IMethodSymbolExtensions.cs │ │ │ ├── IParameterSymbolExtensions.cs │ │ │ ├── ISymbolExtensions.cs │ │ │ └── ITypeSymbolExtensions.cs │ ├── Generators │ │ ├── Generator.cs │ │ ├── Generator{TTarget, TDescendant}.cs │ │ └── TypeEmitter.cs │ ├── Interfaces │ │ ├── Config │ │ │ ├── IAssemblyCachingConfiguration.cs │ │ │ ├── IGeneratorConfiguration.cs │ │ │ └── ILogConfiguration.cs │ │ ├── IDelegateWrapper.cs │ │ ├── IInterceptorAccess.cs │ │ ├── IReferenceCollector.cs │ │ ├── ISupportsSourceGeneration.cs │ │ ├── ITargetAccess.cs │ │ ├── Logging │ │ │ ├── ILogger.cs │ │ │ └── ILoggerFactory.cs │ │ └── Reflection │ │ │ ├── IAssemblyInfo.cs │ │ │ ├── IEventInfo.cs │ │ │ ├── IGeneric.cs │ │ │ ├── IGenericConstraint.cs │ │ │ ├── IHasName.cs │ │ │ ├── IHasType.cs │ │ │ ├── IMemberInfo.cs │ │ │ ├── IMethodInfo.cs │ │ │ ├── IParameterInfo.cs │ │ │ ├── IPropertyInfo.cs │ │ │ └── ITypeInfo.cs │ ├── InvocationContext.cs │ ├── LoadedTypes.cs │ ├── Logging │ │ ├── DebugLogger.cs │ │ ├── FileLogger.cs │ │ ├── LoggerBase.cs │ │ └── LoggerFactory.cs │ ├── PlatformAssemblies.cs │ ├── ReferenceCollector.cs │ ├── Reflection │ │ ├── MetadataAssemblyInfo.cs │ │ ├── MetadataEventInfo.cs │ │ ├── MetadataGenericConstraint.cs │ │ ├── MetadataMethodInfo.cs │ │ ├── MetadataParameterInfo.cs │ │ ├── MetadataPropertyInfo.cs │ │ ├── MetadataTypeInfo.cs │ │ ├── SymbolAssemblyInfo.cs │ │ ├── SymbolEventInfo.cs │ │ ├── SymbolGenericConstraint.cs │ │ ├── SymbolMethodInfo.cs │ │ ├── SymbolParameterInfo.cs │ │ ├── SymbolPropertyInfo.cs │ │ ├── SymbolReturnParameterInfo.cs │ │ └── SymbolTypeInfo.cs │ ├── SourceCode.cs │ ├── SourceGenerators │ │ ├── Diagnostics.cs │ │ ├── ProxyEmbedder.RoslynV3.cs │ │ ├── ProxyEmbedder.RoslynV4.cs │ │ ├── ProxyEmbedderBase.UnitFactory.cs │ │ └── ProxyEmbedderBase.cs │ ├── SyntaxFactories │ │ ├── ClassProxySyntaxFactory.ConstructorFactory.cs │ │ ├── ClassProxySyntaxFactory.EventInterceptorFactory.cs │ │ ├── ClassProxySyntaxFactory.MethodInterceptorFactory.cs │ │ ├── ClassProxySyntaxFactory.PropertyInterceptorFactory.cs │ │ ├── ClassProxySyntaxFactory.cs │ │ ├── ClassSyntaxFactoryBase.Common.cs │ │ ├── ClassSyntaxFactoryBase.Constructor.cs │ │ ├── ClassSyntaxFactoryBase.Event.cs │ │ ├── ClassSyntaxFactoryBase.Field.cs │ │ ├── ClassSyntaxFactoryBase.Member.cs │ │ ├── ClassSyntaxFactoryBase.Method.cs │ │ ├── ClassSyntaxFactoryBase.Property.cs │ │ ├── ClassSyntaxFactoryBase.Variable.cs │ │ ├── ClassSyntaxFactoryBase.cs │ │ ├── DelegateProxySyntaxFactory.Invoke.cs │ │ ├── DelegateProxySyntaxFactory.Wrapped.cs │ │ ├── DelegateProxySyntaxFactory.cs │ │ ├── DuckSyntaxFactory.EventInterceptorFactory.cs │ │ ├── DuckSyntaxFactory.MethodInterceptorFactory.cs │ │ ├── DuckSyntaxFactory.PropertyInterceptorFactory.cs │ │ ├── DuckSyntaxFactory.cs │ │ ├── InterfaceProxySyntaxFactory.EventInterceptorFactory.cs │ │ ├── InterfaceProxySyntaxFactory.MethodInterceptorFactory.cs │ │ ├── InterfaceProxySyntaxFactory.PropertyInterceptorFactory.cs │ │ ├── InterfaceProxySyntaxFactory.cs │ │ ├── ModuleInitializerSyntaxFactory.cs │ │ ├── ProxyUnitSyntaxFactory.Common.cs │ │ ├── ProxyUnitSyntaxFactory.Interceptor.cs │ │ ├── ProxyUnitSyntaxFactory.cs │ │ ├── ProxyUnitSyntaxFactoryBase.Activator.cs │ │ ├── ProxyUnitSyntaxFactoryBase.Constructor.cs │ │ ├── ProxyUnitSyntaxFactoryBase.Initializer.cs │ │ ├── ProxyUnitSyntaxFactoryBase.Target.cs │ │ ├── ProxyUnitSyntaxFactoryBase.cs │ │ ├── SyntaxFactoryBase.Attribute.cs │ │ ├── SyntaxFactoryBase.Type.cs │ │ ├── SyntaxFactoryBase.cs │ │ └── UnitSyntaxFactoryBase.cs │ ├── SyntaxFactoryContext.cs │ └── Visibility.cs ├── Properties │ ├── AssemblyAttributes.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── SGResources.Designer.cs │ └── SGResources.resx ├── ProxyGen.NET.targets ├── ProxyGen.csproj ├── Public │ ├── Attributes │ │ └── EmbedGeneratedTypeAttribute.cs │ ├── ExtendedMemberInfo.cs │ ├── Generators │ │ ├── ClassProxyGenerator.cs │ │ ├── ClassProxyGenerator{TClass}.cs │ │ ├── DelegateProxyGenerator.cs │ │ ├── DelegateProxyGenerator{TDelegate}.cs │ │ ├── DuckGenerator.cs │ │ ├── DuckGenerator{TInterface, TTarget}.cs │ │ ├── InterfaceProxyGenerator.cs │ │ └── InterfaceProxyGenerator{TInterface}.cs │ └── Interfaces │ │ ├── IInterceptor.cs │ │ └── IInvocationContext.cs ├── PublicAPI │ ├── netstandard2.0 │ │ ├── PublicAPI.Shipped.txt │ │ └── PublicAPI.Unshipped.txt │ └── netstandard2.1 │ │ ├── PublicAPI.Shipped.txt │ │ └── PublicAPI.Unshipped.txt ├── docfx.json └── index.md ├── TEST ├── ProxyGen.Tests.EmbeddedTypes │ ├── AssemblyAttributes.cs │ ├── EmbeddedTypeExposer.cs │ ├── IGenericInterfaceHavingConstraint.cs │ ├── IInternalInterface.cs │ ├── InternalDelegate.cs │ ├── InternalFoo.cs │ └── ProxyGen.Tests.EmbeddedTypes.csproj └── ProxyGen.Tests │ ├── BarSrc.txt │ ├── ClsProxySrcModule.txt │ ├── ClsProxySrcUnit.txt │ ├── CodeAnalysisTestsBase.cs │ ├── Compile.cs │ ├── CurrentMember.cs │ ├── Delegate.cs │ ├── DelegateProxySrcModule.txt │ ├── DelegateProxySrcUnit.txt │ ├── DuckClsSrc.txt │ ├── Duck_E40AA6C9C0242588555A55F2A4533DAB.dll │ ├── EventSrc.txt │ ├── FooSrc.txt │ ├── FuncProxySrcModule.txt │ ├── FuncProxySrcUnit.txt │ ├── Generators │ ├── ClassProxyGenerator.cs │ ├── DelegateProxyGenerator.cs │ ├── DuckGenerator.cs │ └── InterfaceProxyGenerator.cs │ ├── IMethodInfoExtensions.cs │ ├── IMethodSymbolExtensions.cs │ ├── ITypeInfoExtensions.cs │ ├── ITypeSymbolExtensions.cs │ ├── IfaceProxySrcModule.txt │ ├── IfaceProxySrcUnit.txt │ ├── IndexerSrc.txt │ ├── MemberInfoExtensions.cs │ ├── MethodInfoExtensions.cs │ ├── ModuleInitializerAttribute.txt │ ├── PropSrc.txt │ ├── ProxyEmbedder.cs │ ├── ProxyGen.Tests.csproj │ ├── Proxy_24AA6ACBF0DFAE64A588818FEE12BF26.dll │ ├── RandomInterfaces.cs │ ├── Reflection.cs │ ├── SyntaxFactories │ ├── ClassProxySyntaxFactory.cs │ ├── ClassSyntaxFactoryBase.cs │ ├── DelegateProxySyntaxFactory.cs │ ├── DuckSyntaxFactory.cs │ ├── InterfaceProxySyntaxFactory.cs │ ├── ModuleInitializerSyntaxFactory.cs │ └── SyntaxFactoryTestsBase.cs │ ├── TypeExtensions.cs │ ├── Visibility.cs │ └── runtimeconfig.template.json ├── appveyor.yml ├── history.md ├── icon.png └── project.json /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "SCRIPTS"] 2 | path = SCRIPTS 3 | url = https://github.com/sholtee/build.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Solti Dénes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PERF/DuckGenerator.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DuckGenerator.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | using BenchmarkDotNet.Attributes; 9 | using BenchmarkDotNet.Engines; 10 | using Moq; 11 | 12 | namespace Solti.Utils.Proxy.Perf 13 | { 14 | using Generators; 15 | using Internals; 16 | 17 | [MemoryDiagnoser] 18 | [SimpleJob(RunStrategy.Throughput, invocationCount: 30000)] 19 | public class DuckGenerator 20 | { 21 | private const int OPERATIONS_PER_INVOKE = 100; 22 | 23 | private static readonly IAssemblyCachingConfiguration FCachingConfiguration = new Mock().Object; 24 | 25 | public class Implementation 26 | { 27 | public int DoSomething(string param) => param.GetHashCode(); 28 | } 29 | 30 | [Benchmark] 31 | public void AssemblingProxyType() => DuckGenerator 32 | .Instance 33 | .EmitAsync(FCachingConfiguration, SyntaxFactoryContext.Default with { AssemblyNameOverride = Guid.NewGuid().ToString() }, default) 34 | .GetAwaiter() 35 | .GetResult(); 36 | 37 | [Benchmark(OperationsPerInvoke = OPERATIONS_PER_INVOKE)] 38 | public void GetGeneratedType() 39 | { 40 | for (int i = 0; i < OPERATIONS_PER_INVOKE; i++) 41 | { 42 | DuckGenerator.GetGeneratedType(); 43 | } 44 | } 45 | 46 | private static readonly Implementation FImplementation = new(); 47 | 48 | [Benchmark(OperationsPerInvoke = OPERATIONS_PER_INVOKE)] 49 | public void Activate() 50 | { 51 | for (int i = 0; i < OPERATIONS_PER_INVOKE; i++) 52 | { 53 | DuckGenerator.Activate(FImplementation); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /PERF/GeneratedDuck.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * GeneratedDuck.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Runtime.CompilerServices; 8 | using System.Threading.Tasks; 9 | 10 | using BenchmarkDotNet.Attributes; 11 | using BenchmarkDotNet.Engines; 12 | 13 | namespace Solti.Utils.Proxy.Perf 14 | { 15 | using Generators; 16 | 17 | [MemoryDiagnoser] 18 | [SimpleJob(RunStrategy.Throughput, invocationCount: 5000000)] 19 | public class GeneratedDuck 20 | { 21 | private const string Param = ""; 22 | 23 | private Implementation FOriginal; 24 | private IInterface FDuck; 25 | 26 | private static async Task CreateDuck(TTarget target) where TInterface: class => 27 | await DuckGenerator.ActivateAsync(target); 28 | 29 | public class Implementation 30 | { 31 | [MethodImpl(MethodImplOptions.NoInlining)] 32 | public int DoSomething(string param) => param.GetHashCode(); 33 | } 34 | 35 | [GlobalSetup(Target = nameof(NoDuck))] 36 | public void SetupNoProxy() => FOriginal = new Implementation(); 37 | 38 | [GlobalSetup(Target = nameof(Duck))] 39 | public async Task SetupThroughProxy() => FDuck = await CreateDuck(new Implementation()); 40 | 41 | [Benchmark(Baseline = true)] 42 | public void NoDuck() => FOriginal.DoSomething(Param); 43 | 44 | [Benchmark] 45 | public void Duck() => FDuck.DoSomething(Param); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /PERF/GeneratedProxy.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * GeneratedProxy.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Reflection; 7 | using System.Runtime.CompilerServices; 8 | using System.Threading.Tasks; 9 | 10 | using BenchmarkDotNet.Attributes; 11 | using BenchmarkDotNet.Engines; 12 | 13 | namespace Solti.Utils.Proxy.Perf 14 | { 15 | using Generators; 16 | 17 | [MemoryDiagnoser] 18 | [SimpleJob(RunStrategy.Throughput, invocationCount: 5000000)] 19 | public class GeneratedProxy 20 | { 21 | private const string Param = ""; 22 | 23 | private IInterface FInstance; 24 | 25 | #region Helper classes 26 | public class Implementation : IInterface 27 | { 28 | [MethodImpl(MethodImplOptions.NoInlining)] 29 | int IInterface.DoSomething(string param) => 0; 30 | } 31 | 32 | private sealed class InterceptorCallingTheTarget : IInterceptor 33 | { 34 | public object Invoke(IInvocationContext context) => context.Dispatch(); 35 | } 36 | 37 | private sealed class InterceptorNotCallingTheTarget: IInterceptor 38 | { 39 | public object Invoke(IInvocationContext context) => 1; 40 | } 41 | 42 | public class DispatchProxyWithTarget : DispatchProxy // DispatchProxy cannot support target 43 | { 44 | protected override object Invoke(MethodInfo targetMethod, object[] args) => 0; 45 | } 46 | #endregion 47 | 48 | [GlobalSetup(Target = nameof(NoProxy))] 49 | public void SetupNoProxy() => FInstance = new Implementation(); 50 | 51 | [GlobalSetup(Target = nameof(ProxyWithTarget))] 52 | public async Task SetupProxy() => FInstance = await InterfaceProxyGenerator.ActivateAsync(new InterceptorCallingTheTarget(), new Implementation()); 53 | 54 | [GlobalSetup(Target = nameof(ProxyWithoutTarget))] 55 | public async Task SetupProxyWithoutTarget() => FInstance = await InterfaceProxyGenerator.ActivateAsync(new InterceptorNotCallingTheTarget()); 56 | 57 | [GlobalSetup(Target = nameof(DispatchProxyWithoutTarget))] 58 | public void SetupDispatchProxyWithoutTarget() => FInstance = DispatchProxy.Create(); 59 | 60 | [Benchmark(Baseline = true)] 61 | public int NoProxy() => FInstance.DoSomething(Param); 62 | 63 | [Benchmark] 64 | public int ProxyWithTarget() => FInstance.DoSomething(Param); 65 | 66 | [Benchmark] 67 | public int ProxyWithoutTarget() => FInstance.DoSomething(Param); 68 | 69 | [Benchmark] 70 | public int DispatchProxyWithoutTarget() => FInstance.DoSomething(Param); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /PERF/IInterface.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IInterface.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Perf 7 | { 8 | public interface IInterface 9 | { 10 | int DoSomething(string param); 11 | } 12 | } -------------------------------------------------------------------------------- /PERF/Program.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * Program.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | #if DEBUG 7 | using BenchmarkDotNet.Configs; 8 | #endif 9 | using BenchmarkDotNet.Running; 10 | 11 | namespace Solti.Utils.Proxy.Perf 12 | { 13 | class Program 14 | { 15 | static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run 16 | ( 17 | args 18 | #if DEBUG 19 | , new DebugInProcessConfig() 20 | #endif 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PERF/ProxyGen.Perf.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | false 6 | net8.0 7 | 8 | ProxyGen.Perf 9 | Solti.Utils.Proxy.Perf 10 | Debug;Release 11 | ..\BIN\$(Configuration)\$(Variant) 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /PERF/ProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ProxyGenerator.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | using BenchmarkDotNet.Attributes; 9 | using BenchmarkDotNet.Engines; 10 | using Moq; 11 | 12 | namespace Solti.Utils.Proxy.Perf 13 | { 14 | using Generators; 15 | using Internals; 16 | 17 | [MemoryDiagnoser] 18 | [SimpleJob(RunStrategy.Throughput, invocationCount: 30000)] 19 | public class ProxyGenerator 20 | { 21 | private const int OPERATIONS_PER_INVOKE = 100; 22 | 23 | private static readonly IAssemblyCachingConfiguration FCachingConfiguration = new Mock().Object; 24 | 25 | private sealed class Interceptor : IInterceptor 26 | { 27 | public object Invoke(IInvocationContext context) => context.Dispatch(); 28 | } 29 | 30 | private static readonly Interceptor FInterceptor = new(); 31 | 32 | [Benchmark] 33 | public void AssemblingProxyType() => InterfaceProxyGenerator 34 | .Instance 35 | .EmitAsync(FCachingConfiguration, SyntaxFactoryContext.Default with { AssemblyNameOverride = Guid.NewGuid().ToString() }, default) 36 | .GetAwaiter() 37 | .GetResult(); 38 | 39 | [Benchmark(OperationsPerInvoke = OPERATIONS_PER_INVOKE)] 40 | public void GetGeneratedType() 41 | { 42 | for (int i = 0; i < OPERATIONS_PER_INVOKE; i++) 43 | { 44 | _ = InterfaceProxyGenerator.GetGeneratedType(); 45 | } 46 | } 47 | 48 | [Benchmark(OperationsPerInvoke = OPERATIONS_PER_INVOKE)] 49 | public void Activate() 50 | { 51 | for (int i = 0; i < OPERATIONS_PER_INVOKE; i++) 52 | { 53 | _ = InterfaceProxyGenerator.Activate(FInterceptor); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /PERF/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "content": [ 4 | { 5 | "files": ["*.md"], 6 | "src": "../Artifacts/BenchmarkDotNet.Artifacts/results" 7 | }, 8 | { 9 | "files": ["index.md"] 10 | } 11 | ], 12 | "dest": "../Artifacts/BenchmarkDotNet.Artifacts/perf", 13 | "resource": [{ 14 | "files": ["icon.png"], 15 | "src": "../" 16 | }], 17 | "globalMetadata": { 18 | "_appTitle": "Benchmark Results", 19 | "_appFaviconPath": "icon.png", 20 | "_disableNavbar": true, 21 | "_disableContribution": true 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /PERF/index.md: -------------------------------------------------------------------------------- 1 | # ProxyGen Benchmark Results 2 | 3 | - [Proxy Generator](https://sholtee.github.io/proxygen/perf/Solti.Utils.Proxy.Perf.ProxyGenerator-report-github.html ) 4 | - [Generated Proxy](https://sholtee.github.io/proxygen/perf/Solti.Utils.Proxy.Perf.GeneratedProxy-report-github.html ) 5 | - [Duck Generator](https://sholtee.github.io/proxygen/perf/Solti.Utils.Proxy.Perf.DuckGenerator-report-github.html ) 6 | - [Generated Duck](https://sholtee.github.io/proxygen/perf/Solti.Utils.Proxy.Perf.GeneratedDuck-report-github.html ) -------------------------------------------------------------------------------- /ProxyGen.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.13.35931.197 d17.13 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyGen", "SRC\ProxyGen.csproj", "{BFEF5090-4419-4972-9773-E39B79C8D10E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyGen.Tests", "TEST\ProxyGen.Tests\ProxyGen.Tests.csproj", "{8BEA3918-352E-43C2-8C65-70C1A5D7F2BB}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyGen.Perf", "PERF\ProxyGen.Perf.csproj", "{4B927F1C-260E-4725-94C4-296F1CC6B29B}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyGen.Tests.EmbeddedTypes", "TEST\ProxyGen.Tests.EmbeddedTypes\ProxyGen.Tests.EmbeddedTypes.csproj", "{5029D512-C599-4C97-B744-E769E9A28C5E}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Perf|Any CPU = Perf|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {BFEF5090-4419-4972-9773-E39B79C8D10E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {BFEF5090-4419-4972-9773-E39B79C8D10E}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {BFEF5090-4419-4972-9773-E39B79C8D10E}.Perf|Any CPU.ActiveCfg = Release|Any CPU 24 | {BFEF5090-4419-4972-9773-E39B79C8D10E}.Perf|Any CPU.Build.0 = Release|Any CPU 25 | {BFEF5090-4419-4972-9773-E39B79C8D10E}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {BFEF5090-4419-4972-9773-E39B79C8D10E}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {8BEA3918-352E-43C2-8C65-70C1A5D7F2BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {8BEA3918-352E-43C2-8C65-70C1A5D7F2BB}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {8BEA3918-352E-43C2-8C65-70C1A5D7F2BB}.Perf|Any CPU.ActiveCfg = Debug|Any CPU 30 | {8BEA3918-352E-43C2-8C65-70C1A5D7F2BB}.Release|Any CPU.ActiveCfg = Debug|Any CPU 31 | {4B927F1C-260E-4725-94C4-296F1CC6B29B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {4B927F1C-260E-4725-94C4-296F1CC6B29B}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {4B927F1C-260E-4725-94C4-296F1CC6B29B}.Perf|Any CPU.ActiveCfg = Release|Any CPU 34 | {4B927F1C-260E-4725-94C4-296F1CC6B29B}.Perf|Any CPU.Build.0 = Release|Any CPU 35 | {4B927F1C-260E-4725-94C4-296F1CC6B29B}.Release|Any CPU.ActiveCfg = Debug|Any CPU 36 | {5029D512-C599-4C97-B744-E769E9A28C5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {5029D512-C599-4C97-B744-E769E9A28C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {5029D512-C599-4C97-B744-E769E9A28C5E}.Perf|Any CPU.ActiveCfg = Debug|Any CPU 39 | {5029D512-C599-4C97-B744-E769E9A28C5E}.Release|Any CPU.ActiveCfg = Debug|Any CPU 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | GlobalSection(ExtensibilityGlobals) = postSolution 45 | SolutionGuid = {7540E3E4-63A1-4431-ADF6-597FB6330DBE} 46 | EndGlobalSection 47 | EndGlobal 48 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sholtee/proxygen/0ecd6213b3d73a567d5d65f6f7c632051ee8343c/README.MD -------------------------------------------------------------------------------- /SRC/Private/Attributes/SupportsSourceGenerationAttributeBase.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SupportsSourceGenerationAttributeBase.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.CSharp; 10 | 11 | namespace Solti.Utils.Proxy.Internals 12 | { 13 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 14 | internal abstract class SupportsSourceGenerationAttributeBase : Attribute, ISupportsSourceGeneration 15 | { 16 | public abstract ProxyUnitSyntaxFactoryBase CreateMainUnit(INamedTypeSymbol generator, CSharpCompilation compilation, SyntaxFactoryContext context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SRC/Private/Comparers/ComparerBase.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ComparerBase.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | // 11 | // Don't use Solti.Utils.Primitives.ComparerBase otherwise we should provide that library for Roslyn too. 12 | // 13 | 14 | internal abstract class ComparerBase : IEqualityComparer where TConcreteComparer : ComparerBase, new() 15 | { 16 | public abstract bool Equals(T x, T y); 17 | 18 | public abstract int GetHashCode(T obj); 19 | 20 | public static TConcreteComparer Instance { get; } = new TConcreteComparer(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SRC/Private/Comparers/MetadataReferenceComparer.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * MetadataReferenceComparer.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | using Microsoft.CodeAnalysis; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal sealed class MetadataReferenceComparer : ComparerBase 13 | { 14 | public override bool Equals(MetadataReference x, MetadataReference y) => 15 | x.Display is not null && 16 | y.Display is not null && 17 | x.Display.Equals(y.Display, StringComparison.OrdinalIgnoreCase); 18 | 19 | public override int GetHashCode(MetadataReference obj) => obj.Display?.GetHashCode() ?? 0; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SRC/Private/Comparers/Reflection/IAssemblyInfoComparer.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IAssemblyInfoComparer.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal sealed class IAssemblyInfoComparer : ComparerBase 11 | { 12 | public override bool Equals(IAssemblyInfo x, IAssemblyInfo y) => x.Name.Equals(y.Name, StringComparison.OrdinalIgnoreCase); 13 | 14 | public override int GetHashCode(IAssemblyInfo obj) => obj.Name.GetHashCode 15 | ( 16 | #if !NETSTANDARD2_0 17 | StringComparison.OrdinalIgnoreCase 18 | #endif 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SRC/Private/Comparers/Reflection/ITypeInfoComparer.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ITypeInfoComparer.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal sealed class ITypeInfoComparer : ComparerBase 11 | { 12 | public override bool Equals(ITypeInfo x, ITypeInfo y) 13 | { 14 | string 15 | name1 = x.AssemblyQualifiedName ?? x.Name, 16 | name2 = y.AssemblyQualifiedName ?? y.Name; 17 | 18 | return name1.Equals(name2, StringComparison.OrdinalIgnoreCase); 19 | } 20 | 21 | public override int GetHashCode(ITypeInfo obj) => (obj.AssemblyQualifiedName ?? obj.Name).GetHashCode 22 | ( 23 | #if !NETSTANDARD2_0 24 | StringComparison.OrdinalIgnoreCase 25 | #endif 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SRC/Private/Comparers/TypeComparer.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * TypeComparer.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | /// 11 | /// Wrapper around the method. 12 | /// 13 | internal sealed class TypeComparer : ComparerBase 14 | { 15 | public override bool Equals(Type x, Type y) => x.EqualsTo(y); 16 | 17 | public override int GetHashCode(Type obj) => throw new NotImplementedException(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SRC/Private/Config/AnalyzerConfig.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * AnalyzerConfig.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using Microsoft.CodeAnalysis.Diagnostics; 7 | using System; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | /// 12 | /// Configuration to be used when the library is loaded as a source generator. 13 | /// 14 | internal sealed class AnalyzerConfig(AnalyzerConfigOptions configOptions) : ConfigBase, IGeneratorConfiguration 15 | { 16 | // 17 | // It seems due to the CompilerVisibleProperty (see ProxyGen.NET.targets) if the name of a particular build 18 | // property is known, the TryGetValue() will return true even if the property is not defined. 19 | // 20 | private string? ReadValueInternal(string name) => configOptions.TryGetValue($"build_property.{name}", out string? value) && !string.IsNullOrEmpty(value) 21 | ? value 22 | : null; 23 | 24 | /// 25 | public bool DebugGenerator => ReadValue(nameof(DebugGenerator))? 26 | .Equals(true.ToString(), StringComparison.OrdinalIgnoreCase) is true; 27 | 28 | /// 29 | protected override string BasePath => ReadValueInternal("MSBuildProjectDirectory")!; 30 | 31 | /// 32 | protected override string? ReadValue(string name) => ReadValueInternal($"ProxyGen_{name}"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SRC/Private/Config/ConfigBase.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ConfigBase.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.IO; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | /// 12 | /// Represents the common configuration. 13 | /// 14 | internal abstract class ConfigBase: ILogConfiguration 15 | { 16 | /// 17 | /// When implemented, reads the value associated with the given from the underlying config source. 18 | /// 19 | protected abstract string? ReadValue(string name); 20 | 21 | /// 22 | /// When implemented returns the containing directory of executable that references the ProxyGen assembly. 23 | /// 24 | protected abstract string BasePath { get; } 25 | 26 | /// 27 | public string? LogDirectory => GetPath(nameof(LogDirectory)); 28 | 29 | /// 30 | public LogLevel LogLevel => Enum.TryParse(ReadValue(nameof(LogLevel)), out LogLevel logLevel) 31 | ? logLevel 32 | : LogLevel.Info; 33 | 34 | /// 35 | /// Gets the path associated with the given name. 36 | /// 37 | protected string? GetPath(string name) 38 | { 39 | string? result = ReadValue(name); 40 | 41 | if (result is not null) 42 | { 43 | result = Environment.ExpandEnvironmentVariables(result); 44 | 45 | if (!Path.IsPathRooted(result)) 46 | result = Path.Combine(BasePath, result); 47 | } 48 | 49 | return result; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /SRC/Private/Config/RuntimeConfig.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * RuntimeConfig.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | /// 11 | /// Configuration read from 12 | /// 13 | internal sealed class RuntimeConfig : ConfigBase, IAssemblyCachingConfiguration 14 | { 15 | /// 16 | public string? AssemblyCacheDir => GetPath(nameof(AssemblyCacheDir)); 17 | 18 | /// 19 | protected override string BasePath { get; } = AppDomain.CurrentDomain.BaseDirectory; 20 | 21 | /// 22 | protected override string? ReadValue(string name) => AppContext.GetData($"ProxyGen.{name}") as string; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SRC/Private/CurrentMember.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * CurrentMember.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Diagnostics; 8 | using System.Reflection; 9 | using System.Runtime.CompilerServices; 10 | 11 | namespace Solti.Utils.Proxy.Internals 12 | { 13 | using Properties; 14 | 15 | /// 16 | /// Contains helpers related to the currently executing member (method, property or event accessor). Intended for private use only. 17 | /// 18 | public static class CurrentMember // this class is referenced by the generated proxies so it must be public 19 | { 20 | /// 21 | /// Gets the base definition of the currently executing member. 22 | /// 23 | /// Returns false if the is not null 24 | [MethodImpl(MethodImplOptions.NoInlining)] 25 | public static bool GetBase(ref ExtendedMemberInfo memberInfo) 26 | { 27 | if (memberInfo is not null) 28 | return false; 29 | 30 | // 31 | // Get the calling method 32 | // 33 | 34 | MethodInfo callingMethod = (MethodInfo) new StackTrace().GetFrame(1).GetMethod(); 35 | 36 | memberInfo = new ExtendedMemberInfo 37 | ( 38 | callingMethod.GetOverriddenMethod() ?? throw new InvalidOperationException(Resources.NOT_VIRTUAL) 39 | ); 40 | 41 | return true; 42 | } 43 | 44 | /// 45 | /// Gets the interface member that is implemented by the currently executing method. 46 | /// 47 | /// Returns false if the is not null 48 | [MethodImpl(MethodImplOptions.NoInlining)] 49 | public static bool GetImplementedInterfaceMethod(ref ExtendedMemberInfo memberInfo) 50 | { 51 | if (memberInfo is not null) 52 | return false; 53 | 54 | // 55 | // Get the calling method 56 | // 57 | 58 | MethodInfo callingMethod = (MethodInfo) new StackTrace().GetFrame(1).GetMethod(); 59 | 60 | MethodInfo[] implementedInterfaceMethods = [.. callingMethod.GetImplementedInterfaceMethods()]; 61 | if (implementedInterfaceMethods.Length is not 1) 62 | throw new InvalidOperationException(string.Format(Resources.AMBIGUOUS_MATCH, callingMethod)); 63 | 64 | memberInfo = new ExtendedMemberInfo(implementedInterfaceMethods[0]); 65 | return true; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /SRC/Private/Enums/LogLevel.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * LogLevel.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal enum LogLevel 9 | { 10 | Debug, 11 | Info, 12 | Warn, 13 | Error 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SRC/Private/Enums/OutputType.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * OutputType.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal enum OutputType 9 | { 10 | Module, 11 | Unit 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SRC/Private/Enums/Reflection/AccessModifiers.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * AccessModifiers.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | [Flags] 11 | internal enum AccessModifiers 12 | { 13 | Unknown = 0, 14 | Private = 1 << 0, 15 | Explicit = 1 << 1, 16 | Protected = 1 << 2, 17 | Internal = 1 << 3, 18 | Public = 1 << 4 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SRC/Private/Enums/Reflection/ParameterKind.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ParameterKind.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal enum ParameterKind 9 | { 10 | None, 11 | Params, 12 | In, 13 | Out, 14 | Ref, 15 | RefReadonly 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SRC/Private/Enums/Reflection/RefType.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * RefType.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | 7 | namespace Solti.Utils.Proxy.Internals 8 | { 9 | internal enum RefType 10 | { 11 | None = 0, 12 | Ref, // Type.IsByRef has no counterpart in INamedTypeInfo (see: PassingByReference_ShouldNotAffectTheParameterType test) so this flag is applied on "ref struct"s only 13 | Pointer, 14 | Array 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SRC/Private/Enums/Reflection/TypeInfoFlags.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * TypeInfoFlags.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | [Flags] 12 | internal enum TypeInfoFlags 13 | { 14 | None = 0, 15 | IsVoid = 1 << 0, 16 | /// 17 | /// The type is nested and not a generic parameter. 18 | /// 19 | IsNested = 1 << 1, 20 | /// 21 | /// The type represents a generic parameter (for e.g.: "T" in ). 22 | /// 23 | IsGenericParameter = 1 << 2, 24 | IsInterface = 1 << 3, 25 | IsClass = 1 << 4, 26 | IsFinal = 1 << 5, 27 | IsAbstract = 1 << 6, 28 | IsDelegate = 1 << 7, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SRC/Private/Exceptions/InvalidSymbolException.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * InvalidSymbolException.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Diagnostics.CodeAnalysis; 8 | 9 | using Microsoft.CodeAnalysis; 10 | 11 | namespace Solti.Utils.Proxy.Internals 12 | { 13 | [SuppressMessage("Design", "CA1064:Exceptions should be public", Justification = "This exception should never reach the end-user.")] 14 | internal class InvalidSymbolException: Exception 15 | { 16 | public InvalidSymbolException(ISymbol symbol) => Symbol = symbol; 17 | 18 | public ISymbol? Symbol { get; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * EnumExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | internal static class EnumExtensions 12 | { 13 | public static IEnumerable SetFlags(this TEnum self) where TEnum: struct, Enum 14 | { 15 | foreach (TEnum flag in Enum.GetValues(typeof(TEnum))) 16 | if (!flag.Equals(default(TEnum)) && self.HasFlag(flag)) 17 | yield return flag; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/ExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ExceptionExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal static class ExceptionExtensions 13 | { 14 | /// 15 | /// Returns true if the provided exception was thrown from user code, false otherwise. 16 | /// 17 | /// The method must be called within a catch block. 18 | public static bool IsUser(this Exception src) => new StackTrace(src) 19 | .GetFrames() 20 | .First() 21 | .GetMethod() 22 | .DeclaringType 23 | .FullName 24 | .StartsWith(typeof(IInterceptor).Namespace); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/HashExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * HashExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Security.Cryptography; 8 | using System.Text; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal static class HashExtensions 13 | { 14 | public static string ToString(this HashAlgorithm self, string format) 15 | { 16 | self.TransformFinalBlock(Array.Empty(), 0, 0); 17 | 18 | StringBuilder sb = new(); 19 | 20 | for (int i = 0; i < self.Hash.Length; i++) 21 | sb.Append(self.Hash[i].ToString(format, null)); 22 | 23 | return sb.ToString(); 24 | } 25 | 26 | public static void Update(this ICryptoTransform transform, string value) 27 | { 28 | byte[] bytes = Encoding.UTF8.GetBytes(value); 29 | transform.TransformBlock(bytes, 0, bytes.Length, bytes, 0); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/IEnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IEnumerableExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal static class IEnumerableExtensions 11 | { 12 | /// 13 | /// Returns the index of a particular within the provided list. Returns -1 if the list doesn't include the . 14 | /// 15 | public static int IndexOf(this IEnumerable src, T item, IEqualityComparer comparer) 16 | { 17 | int index = 0; 18 | 19 | foreach (T i in src) 20 | { 21 | if (comparer.Equals(item, i)) 22 | return index; 23 | index++; 24 | } 25 | return -1; 26 | } 27 | 28 | /// 29 | /// Returns the index of a particular within the provided list using the default . Returns -1 if the list doesn't include the . 30 | /// 31 | public static int IndexOf(this IEnumerable src, T item) => src.IndexOf(item, EqualityComparer.Default); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Metadata/MemberInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * MemberInfoExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Reflection; 7 | using System.Text.RegularExpressions; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | internal static class MemberInfoExtensions 12 | { 13 | private static readonly Regex 14 | // 15 | // The name of explicit implementation is in the form of "Namespace.Interface.Tag" 16 | // 17 | 18 | FStripper = new("([\\w]+)$", RegexOptions.Compiled | RegexOptions.Singleline); 19 | 20 | public static string StrippedName(this MemberInfo self) => FStripper.Match(self.Name).Value; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Metadata/ParameterInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ParameterInfoExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Linq; 8 | using System.Reflection; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal static class ParameterInfoExtensions 13 | { 14 | /// 15 | /// Determines if the parameter possesses the given attribute. The attribute is identified by its fully qualified name since not all attribute types are available in NETSTANDARD. 16 | /// 17 | public static bool HasAttribute(this ParameterInfo src, string fullName) => src 18 | .GetCustomAttributes() 19 | .Select(static attr => attr.GetType().FullName) 20 | .Contains(fullName, StringComparer.OrdinalIgnoreCase); 21 | 22 | /// 23 | /// Associates to the given . 24 | /// 25 | /// This method examines attributes as well so the caller better cache the return value. 26 | public static ParameterKind GetParameterKind(this ParameterInfo src) 27 | { 28 | // 29 | // IsRetval is quirky under netcore3.0 so determine if we have a return 30 | // value by position 31 | // 32 | 33 | if (src.Position is -1) return src switch 34 | { 35 | { ParameterType.IsByRef: true } => IsReadOnly() ? ParameterKind.RefReadonly : ParameterKind.Ref, 36 | _ => ParameterKind.Out 37 | }; 38 | 39 | // 40 | // We have a "regular" parameter 41 | // 42 | 43 | if (src.ParameterType.IsByRef) return src switch 44 | { 45 | // 46 | // "native by ref" (e.g. IntPtr) is out of play from here. 47 | // 48 | 49 | { IsIn: true } when IsReadOnly() => ParameterKind.In, // src.IsIn is not enough 50 | { IsIn: false, IsOut: true } => ParameterKind.Out, 51 | _ => ParameterKind.Ref 52 | }; 53 | 54 | // 55 | // "params" and by ref parameters are mutually exclusives. 56 | // 57 | 58 | if (src.HasAttribute(typeof(ParamArrayAttribute).FullName) || src.HasAttribute("System.Runtime.CompilerServices.ParamCollectionAttribute")) 59 | return ParameterKind.Params; 60 | 61 | return ParameterKind.None; 62 | 63 | bool IsReadOnly() => src.HasAttribute("System.Runtime.CompilerServices.IsReadOnlyAttribute"); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Metadata/PropertyInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * PropertyInfoExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Linq.Expressions; 8 | using System.Reflection; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal static class PropertyInfoExtensions 13 | { 14 | public static PropertyInfo ExtractFrom(Expression> expression) => (PropertyInfo) ((MemberExpression) expression.Body).Member; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/NumberExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * NumberExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | internal static class NumberExtensions 12 | { 13 | public static IEnumerable Times(this int src, Func factory) => src.Times(_ => factory()); 14 | 15 | public static IEnumerable Times(this int src, Func factory) 16 | { 17 | for (int i = 0; i < src; i++) 18 | yield return factory(i); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Reflection/IEventInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IEventInfoExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | internal static class IEventInfoExtensions 12 | { 13 | public static IEnumerable Sort(this IEnumerable self) => self 14 | // 15 | // Events always have add & remove methods assigned 16 | // 17 | 18 | .OrderBy(static e => $"{e.Name}_{e.AddMethod.GetMD5HashCode()}"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Reflection/IGenericConstraintExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IGenericConstraintExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Linq; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal static class IGenericConstraintExtensions 11 | { 12 | public static bool EqualsTo(this IGenericConstraint @this, IGenericConstraint that) 13 | { 14 | if (@this.ConstraintTypes.Count != that.ConstraintTypes.Count) 15 | return false; 16 | 17 | foreach (ITypeInfo c1 in @this.ConstraintTypes) 18 | if (!that.ConstraintTypes.Any(c1.EqualsTo)) 19 | return false; 20 | 21 | return 22 | @this.Reference == that.Reference && 23 | @this.DefaultConstructor == that.DefaultConstructor && 24 | @this.Struct == that.Struct; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Reflection/IParameterInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IParameterInfoExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal static class IParameterInfoExtensions 9 | { 10 | public static bool EqualsTo(this IParameterInfo @this, IParameterInfo that) => 11 | // 12 | // Not checking the name is intentional 13 | // 14 | 15 | @this.Kind == that.Kind && @this.Type.EqualsTo(that.Type); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Reflection/IPropertyInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IPropertyInfoExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | internal static class IPropertyInfoExtensions 12 | { 13 | public static IEnumerable Sort(this IEnumerable self) => self.OrderBy(static p => 14 | { 15 | List accessors = []; 16 | if (p.SetMethod is not null) 17 | accessors.Add(p.SetMethod); 18 | if (p.GetMethod is not null) 19 | accessors.Add(p.GetMethod); 20 | 21 | return $"{p.Name}_{accessors.GetMD5HashCode()}"; 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * StreamExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.IO; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal static class StreamExtensions 11 | { 12 | public static byte[] ToArray(this Stream self) 13 | { 14 | byte[] buffer = new byte[self.Length]; 15 | self.Read(buffer, 0, buffer.Length); 16 | return buffer; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/StreamWriterExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * StreamWriterExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Diagnostics.CodeAnalysis; 8 | using System.IO; 9 | using System.Threading; 10 | 11 | namespace Solti.Utils.Proxy.Internals 12 | { 13 | internal static class StreamWriterExtensions 14 | { 15 | [SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "The 'cancellation' is supported in NETSTANDARD2_1+ only")] 16 | public static void Write(this StreamWriter self, string str, CancellationToken cancellation) => 17 | #if NETSTANDARD2_0 18 | self.Write(str); 19 | #else 20 | self.WriteAsync(str.AsMemory(), cancellation).Wait(CancellationToken.None); 21 | #endif 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Symbol/IParameterSymbolExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IParameterSymbolExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using Microsoft.CodeAnalysis; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal static class IParameterSymbolExtensions 11 | { 12 | /// 13 | /// Associates to the given . 14 | /// 15 | public static ParameterKind GetParameterKind(this IParameterSymbol src) => src switch 16 | { 17 | { IsParams: true } => ParameterKind.Params, 18 | { RefKind: RefKind.RefReadOnly} => ParameterKind.In, 19 | { RefKind: RefKind.Out} => ParameterKind.Out, 20 | { RefKind: RefKind.Ref 21 | #if !LEGACY_COMPILER 22 | or RefKind.RefReadOnlyParameter 23 | #endif 24 | } => ParameterKind.Ref, 25 | _ => ParameterKind.None 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SRC/Private/Extensions/Symbol/ISymbolExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ISymbolExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Text.RegularExpressions; 7 | 8 | using Microsoft.CodeAnalysis; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal static class ISymbolExtensions 13 | { 14 | // 15 | // In case of explicit implementation the name is in form of "Interface.Tag" 16 | // 17 | 18 | private static readonly Regex FStripper = new("(\\w+)$", RegexOptions.Compiled | RegexOptions.Singleline); 19 | 20 | public static string StrippedName(this ISymbol self) => FStripper.Match(self.MetadataName).Value; 21 | 22 | public static void EnsureNotError(this ISymbol self) 23 | { 24 | if (self.Kind is SymbolKind.ErrorType) 25 | throw new InvalidSymbolException(self); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SRC/Private/Generators/Generator{TTarget, TDescendant}.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * Generator.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Diagnostics.CodeAnalysis; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace Solti.Utils.Proxy.Internals 12 | { 13 | /// 14 | /// Base of typed generators. 15 | /// 16 | /// Generators should not be instantiated. To access the created use the or method. 17 | [SuppressMessage("Design", "CA1000:Do not declare static members on generic types")] 18 | public abstract class Generator where TDescendant: Generator, new() where TUntypedGenerator: Generator 19 | { 20 | /// 21 | /// Gets the concrete generator. 22 | /// 23 | protected abstract TUntypedGenerator GetConcreteGenerator(); 24 | 25 | /// 26 | /// The singleton generator instance. 27 | /// 28 | public static TUntypedGenerator Instance { get; } = new TDescendant().GetConcreteGenerator(); 29 | 30 | /// 31 | /// Gets the generated asynchronously. 32 | /// 33 | /// The returned is generated only once. 34 | public static Task GetGeneratedTypeAsync(CancellationToken cancellation = default) => Instance.GetGeneratedTypeAsync(cancellation); 35 | 36 | /// 37 | /// Gets the generated . 38 | /// 39 | /// The returned is generated only once. 40 | public static Type GetGeneratedType() => Instance.GetGeneratedType(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Config/IAssemblyCachingConfiguration.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IAssemblyCachingConfiguration.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface IAssemblyCachingConfiguration 9 | { 10 | string? AssemblyCacheDir { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Config/IGeneratorConfiguration.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IGeneratorConfiguration.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface IGeneratorConfiguration 9 | { 10 | bool DebugGenerator { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Config/ILogConfiguration.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ILogConfiguration.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | /// 9 | /// Describes the required configuration values related to logging 10 | /// 11 | internal interface ILogConfiguration 12 | { 13 | /// 14 | /// Where to put the logs, or null if we don't need them. 15 | /// 16 | string? LogDirectory { get; } 17 | 18 | /// 19 | /// Log level to be used. Considered only when the is set. 20 | /// 21 | LogLevel LogLevel { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/IDelegateWrapper.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IDelegateWrapper.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | /// 11 | /// Contract that specifies how to get the wrapped delegate 12 | /// 13 | /// This interface is used by the generated proxies only therefore it is considered internal. User code should not depend on it. 14 | public interface IDelegateWrapper 15 | { 16 | /// 17 | /// The wrapped delegate 18 | /// 19 | Delegate Wrapped { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/IInterceptorAccess.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IInterceptorAccess.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | /// 9 | /// Type independent way to access the underlying interceptor. 10 | /// 11 | /// This interface is used by the generated proxies only therefore it is considered internal. User code should not depend on it. 12 | public interface IInterceptorAccess 13 | { 14 | /// 15 | /// The interceptor instance 16 | /// 17 | IInterceptor Interceptor { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /SRC/Private/Interfaces/IReferenceCollector.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IReferenceCollector.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | /// 11 | /// Defines the contract how to collect references. 12 | /// 13 | internal interface IReferenceCollector 14 | { 15 | /// 16 | /// Registers a new type (and its assembly). In case of generic types this method should call itself to generic arguments recursively. If the type is already known this method is a noop 17 | /// 18 | void AddType(ITypeInfo type); 19 | 20 | /// 21 | /// Types encountered by this collector. Each list entry should be unique, in other words this is a distinct list. 22 | /// 23 | IReadOnlyCollection Types { get; } 24 | 25 | /// 26 | /// Assemblies encountered by this collector. Each list entry should be unique, in other words this is a distinct list. 27 | /// 28 | IReadOnlyCollection References { get; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/ISupportsSourceGeneration.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ISupportsSourceGeneration.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.CSharp; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | internal interface ISupportsSourceGeneration 12 | { 13 | ProxyUnitSyntaxFactoryBase CreateMainUnit(INamedTypeSymbol generator, CSharpCompilation compilation, SyntaxFactoryContext context); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/ITargetAccess.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ITargetAccess.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | /// 9 | /// Type independent way to access the underlying target. 10 | /// 11 | /// This interface is used by the generated proxies only therefore it is considered internal. User code should not depend on it. 12 | public interface ITargetAccess 13 | { 14 | /// 15 | /// The target instance. 16 | /// 17 | object? Target { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Logging/ILogger.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ILogger.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | using Microsoft.CodeAnalysis.CSharp.Syntax; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | /// 13 | /// The logger maintains two separate logs. The 1st contains collection of system logs, the 2nd is for the source being crafted. 14 | /// 15 | internal interface ILogger 16 | { 17 | /// 18 | /// Creates/overwrites the source log. 19 | /// 20 | void WriteSource(CompilationUnitSyntax src); 21 | 22 | /// 23 | /// Appends the system log 24 | /// 25 | void Log(LogLevel level, object id, string message, IDictionary? additionalData = null); 26 | 27 | /// 28 | /// The minimum log level under which the logger should not dump information or null when logging is turned off. 29 | /// 30 | LogLevel? Level { get; } 31 | 32 | /// 33 | /// The scope to be used. For instance when writing physical logs this id will be used in output file names: "{Scope}.log" and "{Scope}.cs" 34 | /// 35 | string Scope { get; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Logging/ILoggerFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ILoggerFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface ILoggerFactory 9 | { 10 | /// 11 | /// Creates a new logger using the given log . 12 | /// 13 | ILogger CreateLogger(string scope); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IAssemblyInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface IAssemblyInfo 9 | { 10 | string? Location { get; } 11 | bool IsDynamic { get; } 12 | string Name { get; } 13 | bool IsFriend(string asmName); 14 | ITypeInfo? GetType(string fullName); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IEventInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IEventInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface IEventInfo: IMemberInfo, IHasType 9 | { 10 | IMethodInfo AddMethod { get; } 11 | IMethodInfo RemoveMethod { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IGeneric.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IGeneric.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal interface IGeneric where TDescendant: IGeneric 11 | { 12 | /// 13 | /// Returns true for unbound generics false otherwise. 14 | /// 15 | bool IsGenericDefinition { get; } 16 | /// 17 | /// Gets the unbound definition. 18 | /// 19 | TDescendant GenericDefinition { get; } 20 | /// 21 | /// Substitutes the type arguments with the values provided by the parameter. 22 | /// 23 | TDescendant Close(params ITypeInfo[] genericArgs); 24 | /// 25 | /// Returns the explicitly declared generic arguments (e.g.: in case of Generic{T}.Nested{TT} this property returns TT for the nested type). 26 | /// 27 | IReadOnlyList GenericArguments { get; } 28 | /// 29 | /// A list of objects. 30 | /// 31 | IReadOnlyList GenericConstraints { get; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IGenericConstraint.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IGenericConstraint.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal interface IGenericConstraint 11 | { 12 | /// 13 | /// The type must have a default constructor: where T: new() 14 | /// 15 | bool DefaultConstructor { get; } 16 | /// 17 | /// The type must be a reference type: where T: class 18 | /// 19 | bool Reference { get; } 20 | /// 21 | /// The type must be a value type: where T: struct 22 | /// 23 | bool Struct { get; } 24 | 25 | ITypeInfo Target { get; } 26 | 27 | IReadOnlyList ConstraintTypes { get; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IHasName.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IHasName.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface IHasName 9 | { 10 | /// 11 | /// The name of the member not containing special characters ("+", "`", "<", ">"), namespace for nested types or interface part of explicit implementations. 12 | /// 13 | string Name { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IHasType.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IHasType.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface IHasType 9 | { 10 | ITypeInfo Type { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IMemberInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IMemberInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface IMemberInfo: IHasName 9 | { 10 | ITypeInfo DeclaringType { get; } 11 | bool IsStatic { get; } 12 | bool IsAbstract { get; } 13 | bool IsVirtual { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IMethodInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IMethodInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal interface IMethodInfo: IMemberInfo 11 | { 12 | IReadOnlyList Parameters { get; } 13 | IReadOnlyList DeclaringInterfaces { get; } 14 | IParameterInfo ReturnValue { get; } 15 | bool IsSpecial { get; } 16 | AccessModifiers AccessModifiers { get; } 17 | } 18 | 19 | internal interface IConstructorInfo : IMethodInfo { } 20 | 21 | internal interface IGenericMethodInfo : IMethodInfo, IGeneric { } 22 | } 23 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IParameterInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IParameterInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal interface IParameterInfo: IHasName, IHasType 9 | { 10 | ParameterKind Kind { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/IPropertyInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IPropertyInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal interface IPropertyInfo: IMemberInfo, IHasType 11 | { 12 | IMethodInfo? GetMethod { get; } 13 | IMethodInfo? SetMethod { get; } 14 | IReadOnlyList Indices { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SRC/Private/Interfaces/Reflection/ITypeInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ITypeInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal interface ITypeInfo: IHasName 11 | { 12 | IAssemblyInfo? DeclaringAssembly { get; } 13 | AccessModifiers AccessModifiers { get; } 14 | TypeInfoFlags Flags { get; } 15 | RefType RefType { get; } 16 | /// 17 | /// The of the type followed by the identity of the containing assembly. The result should not reflect the type arguments. 18 | /// 19 | string? AssemblyQualifiedName { get; } 20 | /// 21 | /// The name of the type including its enclosing types and namespace. The result should not reflect the type arguments. 22 | /// 23 | /// The value returned by this property is suitable to be passed to or . 24 | string? QualifiedName { get; } 25 | /// 26 | /// Returns the underlying element type (for e.g.: for int*). Yields non null for pointers, ref types and arrays. 27 | /// 28 | ITypeInfo? ElementType { get; } 29 | IHasName? ContainingMember { get; } 30 | ITypeInfo? EnclosingType { get; } 31 | IReadOnlyList Interfaces { get; } 32 | ITypeInfo? BaseType { get; } 33 | IReadOnlyList Properties { get; } 34 | IReadOnlyList Events { get; } 35 | IReadOnlyList Methods { get; } 36 | IReadOnlyList Constructors { get; } 37 | } 38 | 39 | internal interface IGenericTypeInfo : ITypeInfo, IGeneric { } 40 | 41 | internal interface IArrayTypeInfo : ITypeInfo 42 | { 43 | int Rank { get; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SRC/Private/InvocationContext.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * InvocationContext.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics.CodeAnalysis; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | /// 13 | /// The default implementation of the interface. 14 | /// 15 | /// 16 | /// Creates a new instance. 17 | /// 18 | public sealed class InvocationContext(object proxy, ExtendedMemberInfo targetMember, Func dispatch, object?[] args, IReadOnlyList genericArguments) : IInvocationContext // this class is referenced by the generated proxies so it must be public 19 | { 20 | /// 21 | public object Proxy { get; } = proxy ?? throw new ArgumentNullException(nameof(proxy)); 22 | 23 | /// 24 | public ExtendedMemberInfo Member { get; } = targetMember ?? throw new ArgumentNullException(nameof(targetMember)); 25 | 26 | /// 27 | /// Gets the dispatcher function. 28 | /// 29 | public Func Dispatch { get; } = dispatch ?? throw new ArgumentNullException(nameof(dispatch)); 30 | 31 | /// 32 | [SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "End user is allowed to modify the argument list.")] 33 | public object?[] Args { get; } = args ?? throw new ArgumentNullException(nameof(args)); 34 | 35 | /// 36 | public IReadOnlyList GenericArguments { get; } = genericArguments ?? throw new ArgumentNullException(nameof(genericArguments)); 37 | 38 | object? IInvocationContext.Dispatch() => Dispatch(Args); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SRC/Private/LoadedTypes.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * LoadedTypes.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Concurrent; 8 | using System.Diagnostics; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | /// 13 | /// Describes a loaded generated . 14 | /// 15 | public record TypeContext(Type Type, Func Activator); 16 | 17 | /// 18 | /// Contains the loaded proxy s 19 | /// 20 | public static class LoadedTypes // this class is referenced by the generated proxies so it must be public 21 | { 22 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 23 | private static readonly ConcurrentDictionary FStore = new(); 24 | 25 | /// 26 | /// Tries to grab the proxy by its name. 27 | /// 28 | internal static bool TryGet(string name, out TypeContext type) => FStore.TryGetValue(name, out type); 29 | 30 | /// 31 | /// Registers a new alongside its activator 32 | /// 33 | public static void Register(Type type, Func activator) 34 | { 35 | if (!FStore.TryAdd(type.Name, new TypeContext(type ?? throw new ArgumentNullException(nameof(type)), activator ?? throw new ArgumentNullException(nameof(activator))))) 36 | { 37 | Trace.TraceWarning($"Type already loaded: {type.Name}"); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /SRC/Private/Logging/DebugLogger.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DebugLogger.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Diagnostics; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | /// 11 | /// Logger that writes the input to the debug output. Note that for production builds this logger does nothing since under the hood it uses the method. 12 | /// 13 | internal sealed class DebugLogger(string scope) : LoggerBase(scope, LogLevel.Debug) 14 | { 15 | protected override void WriteSourceCore(string src) => Debug.WriteLine(src); 16 | 17 | protected override void LogCore(string message) => Debug.WriteLine(message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SRC/Private/Logging/FileLogger.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * FileLogger.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.IO; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | /// 12 | /// Logger that outputs the logs to a file 13 | /// 14 | internal sealed class FileLogger : LoggerBase, IDisposable 15 | { 16 | private readonly StreamWriter FLogWriter; 17 | private readonly string FLogDirectory; 18 | 19 | protected override void LogCore(string message) => 20 | FLogWriter.WriteLine(message); 21 | 22 | protected override void WriteSourceCore(string src) => 23 | // 24 | // Overwrite the target file for every invocation 25 | // 26 | 27 | File.WriteAllText(Path.Combine(FLogDirectory, $"{Scope}.cs"), src); 28 | 29 | public FileLogger(string scope, ILogConfiguration config): base($"{scope} - {Environment.CurrentManagedThreadId}", config.LogLevel) 30 | { 31 | FLogDirectory = config.LogDirectory!; 32 | 33 | Directory.CreateDirectory(FLogDirectory); 34 | 35 | FLogWriter = File.CreateText(Path.Combine(FLogDirectory, $"{Scope}.log")); 36 | FLogWriter.AutoFlush = true; 37 | } 38 | 39 | /// 40 | /// Disposes this instance. Since this is an internal class we won't implement the disposable pattern. 41 | /// 42 | public void Dispose() => FLogWriter?.Dispose(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SRC/Private/Logging/LoggerBase.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * LoggerBase.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Runtime.Serialization.Json; 10 | using System.Text; 11 | 12 | using Microsoft.CodeAnalysis; 13 | using Microsoft.CodeAnalysis.CSharp.Syntax; 14 | 15 | namespace Solti.Utils.Proxy.Internals 16 | { 17 | internal abstract class LoggerBase(string scope, LogLevel? level) : ILogger 18 | { 19 | private readonly DataContractJsonSerializer FSerializer = new 20 | ( 21 | typeof(IDictionary), 22 | new DataContractJsonSerializerSettings 23 | { 24 | UseSimpleDictionaryFormat = true, 25 | KnownTypes = [typeof(List)] 26 | } 27 | ); 28 | 29 | protected virtual string Stringify(IDictionary additionalData) 30 | { 31 | using MemoryStream stm = new(); 32 | 33 | FSerializer.WriteObject(stm, additionalData); 34 | 35 | return Encoding.UTF8.GetString(stm.GetBuffer(), 0, (int) stm.Length); 36 | } 37 | 38 | protected abstract void LogCore(string message); 39 | 40 | protected abstract void WriteSourceCore(string src); 41 | 42 | public LogLevel? Level { get; } = level; 43 | 44 | public string Scope { get; } = scope; 45 | 46 | public void Log(LogLevel level, object id, string message, IDictionary? additionalData) 47 | { 48 | if (level < Level) 49 | return; 50 | 51 | string msg = $"{DateTime.UtcNow:o} [{level}] {id} - {message}"; 52 | if (additionalData is not null) 53 | msg += $"{Environment.NewLine} {Stringify(additionalData)}"; 54 | 55 | LogCore(msg); 56 | } 57 | 58 | public void WriteSource(CompilationUnitSyntax src) => WriteSourceCore 59 | ( 60 | src 61 | .NormalizeWhitespace(eol: Environment.NewLine) 62 | .ToFullString() 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /SRC/Private/Logging/LoggerFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * LoggerFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | /// 12 | /// The default implementation of the interface. 13 | /// 14 | /// To create a pass null for 15 | internal sealed class LoggerFactory(ILogConfiguration? configuration) : ILoggerFactory, IDisposable 16 | { 17 | private readonly Stack FDisposables = []; 18 | 19 | public ILogger CreateLogger(string scope) 20 | { 21 | if (configuration?.LogDirectory is not null) 22 | { 23 | FileLogger logger = new(scope, configuration); 24 | FDisposables.Push(logger); 25 | return logger; 26 | } 27 | 28 | return new DebugLogger(scope); 29 | } 30 | 31 | public void Dispose() 32 | { 33 | while (FDisposables.Count > 0) 34 | FDisposables.Pop().Dispose(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SRC/Private/PlatformAssemblies.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * PlatformAssemblies.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Reflection; 11 | 12 | using Microsoft.CodeAnalysis; 13 | 14 | namespace Solti.Utils.Proxy.Internals 15 | { 16 | internal static class PlatformAssemblies 17 | { 18 | private static IEnumerable Read() 19 | { 20 | Assembly netstandard = AppDomain 21 | .CurrentDomain 22 | .GetAssemblies() 23 | .Single(static asm => asm.GetName().Name == "netstandard"); 24 | 25 | string baseDir = Path.GetDirectoryName(netstandard.Location); 26 | 27 | return netstandard 28 | .GetReferencedAssemblies() 29 | .Select(asm => Path.Combine(baseDir, $"{asm.Name}.dll")) 30 | .Where(File.Exists) 31 | .Concat([netstandard.Location]) 32 | .OrderBy(Path.GetFileName) 33 | .Select(static loc => MetadataReference.CreateFromFile(loc)); 34 | } 35 | 36 | public static IReadOnlyCollection References { get; } = [..Read()]; 37 | } 38 | } -------------------------------------------------------------------------------- /SRC/Private/ReferenceCollector.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ReferenceCollector.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | 11 | namespace Solti.Utils.Proxy.Internals 12 | { 13 | using Properties; 14 | 15 | internal sealed class ReferenceCollector: IReferenceCollector 16 | { 17 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 18 | private readonly HashSet FReferences = new(IAssemblyInfoComparer.Instance); 19 | public IReadOnlyCollection References => [..FReferences.OrderBy(static r => r.Name)]; 20 | 21 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 22 | private readonly HashSet FTypes = new(ITypeInfoComparer.Instance); 23 | public IReadOnlyCollection Types => [..FTypes.OrderBy(static t => t.QualifiedName)]; 24 | 25 | public void AddType(ITypeInfo type) 26 | { 27 | if (type.Flags.HasFlag(TypeInfoFlags.IsGenericParameter)) 28 | return; 29 | 30 | if (!FTypes.Add(type)) // circular reference fix 31 | return; 32 | 33 | IAssemblyInfo? asm = type.DeclaringAssembly; 34 | 35 | if (asm is not null) 36 | { 37 | if (asm.IsDynamic) 38 | throw new NotSupportedException(Resources.DYNAMIC_ASM); 39 | 40 | FReferences.Add(asm); 41 | } 42 | 43 | // 44 | // The base can be provided by a different assembly. 45 | // 46 | 47 | if (type.BaseType is not null) 48 | AddType(type.BaseType); 49 | 50 | // 51 | // The generic parameters of enclosing type(s) may come from a different assembly. 52 | // 53 | 54 | if (type.EnclosingType is not null) 55 | AddType(type.EnclosingType); 56 | 57 | // 58 | // Generic parameters may come from different assemblies. 59 | // 60 | 61 | if (type is IGenericTypeInfo genericType) 62 | AddTypesFrom(genericType.GenericArguments); 63 | 64 | // 65 | // Interfaces may be also defined in a different assembly. 66 | // 67 | 68 | AddTypesFrom(type.Interfaces); 69 | 70 | void AddTypesFrom(IEnumerable types) 71 | { 72 | foreach (ITypeInfo type in types) 73 | { 74 | AddType(type); 75 | } 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /SRC/Private/Reflection/MetadataAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * MetadataAssemblyInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Reflection; 8 | using System.Runtime.CompilerServices; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal sealed class MetadataAssemblyInfo : IAssemblyInfo 13 | { 14 | private Assembly UnderlyingAssembly { get; } 15 | 16 | private MetadataAssemblyInfo(Assembly assembly) => UnderlyingAssembly = assembly; 17 | 18 | public override bool Equals(object obj) => obj is MetadataAssemblyInfo that && UnderlyingAssembly.Equals(that.UnderlyingAssembly); 19 | 20 | public override int GetHashCode() => UnderlyingAssembly.GetHashCode(); 21 | 22 | public override string ToString() => UnderlyingAssembly.ToString(); 23 | 24 | public static IAssemblyInfo CreateFrom(Assembly assembly) => new MetadataAssemblyInfo(assembly); 25 | 26 | public bool IsFriend(string asmName) 27 | { 28 | // 29 | // TODO: strong name support 30 | // 31 | 32 | if (StringComparer.OrdinalIgnoreCase.Equals(asmName, UnderlyingAssembly.GetName().Name)) 33 | return true; 34 | 35 | foreach (InternalsVisibleToAttribute ivt in UnderlyingAssembly.GetCustomAttributes()) 36 | { 37 | if (StringComparer.OrdinalIgnoreCase.Equals(asmName, ivt.AssemblyName)) 38 | return true; 39 | } 40 | 41 | return false; 42 | } 43 | 44 | public ITypeInfo? GetType(string fullName) 45 | { 46 | Type type = UnderlyingAssembly.GetType(fullName, throwOnError: false); 47 | 48 | return type is not null 49 | ? MetadataTypeInfo.CreateFrom(type) 50 | : null; 51 | } 52 | 53 | public string? Location => IsDynamic ? null : UnderlyingAssembly.Location; 54 | 55 | public bool IsDynamic => UnderlyingAssembly.IsDynamic; 56 | 57 | public string Name => UnderlyingAssembly.GetName().ToString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SRC/Private/Reflection/MetadataEventInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * MetadataEventInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Diagnostics; 7 | using System.Reflection; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | internal sealed class MetadataEventInfo : IEventInfo 12 | { 13 | private EventInfo UnderlyingEvent { get; } 14 | 15 | public string Name => UnderlyingEvent.StrippedName(); 16 | 17 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 18 | private ITypeInfo? FDeclaringType; 19 | public ITypeInfo DeclaringType => FDeclaringType ??= (AddMethod ?? RemoveMethod).DeclaringType; 20 | 21 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 22 | private IMethodInfo? FAddMethod; 23 | public IMethodInfo AddMethod => FAddMethod ??= MetadataMethodInfo.CreateFrom(UnderlyingEvent.AddMethod); 24 | 25 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 26 | private IMethodInfo? FRemoveMethod; 27 | public IMethodInfo RemoveMethod => FRemoveMethod ??= MetadataMethodInfo.CreateFrom(UnderlyingEvent.RemoveMethod); 28 | 29 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 30 | private ITypeInfo? FType; 31 | public ITypeInfo Type => FType ??= MetadataTypeInfo.CreateFrom(UnderlyingEvent.EventHandlerType); 32 | 33 | public bool IsStatic => (AddMethod ?? RemoveMethod).IsStatic; 34 | 35 | public bool IsAbstract => (AddMethod ?? RemoveMethod).IsAbstract; 36 | 37 | public bool IsVirtual => (AddMethod ?? RemoveMethod).IsVirtual; 38 | 39 | private MetadataEventInfo(EventInfo evt) => UnderlyingEvent = evt; 40 | 41 | public static IEventInfo CreateFrom(EventInfo evt) => new MetadataEventInfo(evt); 42 | 43 | public override bool Equals(object obj) => obj is MetadataEventInfo that && UnderlyingEvent.Equals(that.UnderlyingEvent); 44 | 45 | public override int GetHashCode() => UnderlyingEvent.GetHashCode(); 46 | 47 | public override string ToString() => UnderlyingEvent.ToString(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SRC/Private/Reflection/MetadataGenericConstraint.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * MetadataGenericConstraint.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Collections.Immutable; 9 | using System.Diagnostics; 10 | using System.Linq; 11 | using System.Reflection; 12 | 13 | namespace Solti.Utils.Proxy.Internals 14 | { 15 | internal sealed class MetadataGenericConstraint: IGenericConstraint 16 | { 17 | private Type UnderlyingType { get; } 18 | 19 | private MemberInfo DeclaringMember { get; } 20 | 21 | private MetadataGenericConstraint(Type genericArgument, MemberInfo declaringMember) 22 | { 23 | UnderlyingType = genericArgument; 24 | DeclaringMember = declaringMember; 25 | } 26 | 27 | public static IGenericConstraint? CreateFrom(Type genericArgument, MemberInfo declaringMember) => 28 | genericArgument.GenericParameterAttributes is > GenericParameterAttributes.VarianceMask and < (GenericParameterAttributes) 32 /*AllowByRefLike*/ || genericArgument.GetGenericConstraints(declaringMember).Any() 29 | ? new MetadataGenericConstraint(genericArgument, declaringMember) 30 | : null; 31 | 32 | public bool DefaultConstructor => !Struct && UnderlyingType 33 | .GenericParameterAttributes 34 | .HasFlag(GenericParameterAttributes.DefaultConstructorConstraint); 35 | 36 | public bool Reference => UnderlyingType 37 | .GenericParameterAttributes 38 | .HasFlag(GenericParameterAttributes.ReferenceTypeConstraint); 39 | 40 | public bool Struct => UnderlyingType 41 | .GenericParameterAttributes 42 | .HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint); 43 | 44 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 45 | private IReadOnlyList? FConstraintTypes; 46 | public IReadOnlyList ConstraintTypes => FConstraintTypes ??= UnderlyingType 47 | .GetGenericConstraints(DeclaringMember) 48 | .Select(MetadataTypeInfo.CreateFrom) 49 | .ToImmutableList(); 50 | 51 | private ITypeInfo? FTarget; 52 | public ITypeInfo Target => FTarget ??= MetadataTypeInfo.CreateFrom(UnderlyingType); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /SRC/Private/Reflection/MetadataParameterInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * MetadataParameterInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Diagnostics; 7 | using System.Reflection; 8 | 9 | namespace Solti.Utils.Proxy.Internals 10 | { 11 | internal sealed class MetadataParameterInfo : IParameterInfo 12 | { 13 | private ParameterInfo UnderlyingParameter { get; } 14 | 15 | private MetadataParameterInfo(ParameterInfo para) => UnderlyingParameter = para; 16 | 17 | public static IParameterInfo CreateFrom(ParameterInfo para) => new MetadataParameterInfo(para); 18 | 19 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 20 | private ITypeInfo? FType; 21 | public ITypeInfo Type => FType ??= MetadataTypeInfo.CreateFrom(UnderlyingParameter.ParameterType); 22 | 23 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 24 | private ParameterKind? FKind; 25 | public ParameterKind Kind => FKind ??= UnderlyingParameter.GetParameterKind(); 26 | 27 | public string Name => UnderlyingParameter.Name ?? string.Empty; 28 | 29 | public override bool Equals(object obj) => obj is MetadataParameterInfo that && UnderlyingParameter.Equals(that.UnderlyingParameter); 30 | 31 | public override int GetHashCode() => UnderlyingParameter.GetHashCode(); 32 | 33 | public override string ToString() => UnderlyingParameter.ToString(); 34 | } 35 | } -------------------------------------------------------------------------------- /SRC/Private/Reflection/MetadataPropertyInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * MetadataPropertyInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | using System.Collections.Immutable; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | using System.Reflection; 11 | 12 | namespace Solti.Utils.Proxy.Internals 13 | { 14 | internal sealed class MetadataPropertyInfo : IPropertyInfo 15 | { 16 | private PropertyInfo UnderlyingProperty { get; } 17 | 18 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 19 | private IMethodInfo? FGetMethod; 20 | public IMethodInfo? GetMethod => UnderlyingProperty.GetMethod is not null 21 | ? FGetMethod ??= MetadataMethodInfo.CreateFrom(UnderlyingProperty.GetMethod) 22 | : null; 23 | 24 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 25 | private IMethodInfo? FSetMethod; 26 | public IMethodInfo? SetMethod => UnderlyingProperty.SetMethod is not null 27 | ? FSetMethod ??= MetadataMethodInfo.CreateFrom(UnderlyingProperty.SetMethod) 28 | : null; 29 | 30 | public string Name => UnderlyingProperty.StrippedName(); 31 | 32 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 33 | private ITypeInfo? FType; 34 | public ITypeInfo Type => FType ??= MetadataTypeInfo.CreateFrom(UnderlyingProperty.PropertyType); 35 | 36 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 37 | private ITypeInfo? FDeclaringType; 38 | public ITypeInfo DeclaringType => FDeclaringType ??= (GetMethod ?? SetMethod!).DeclaringType; 39 | 40 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 41 | private IReadOnlyList? FIndices; 42 | public IReadOnlyList Indices => FIndices ??= UnderlyingProperty 43 | .GetIndexParameters() 44 | .Select(MetadataParameterInfo.CreateFrom) 45 | .ToImmutableList(); 46 | 47 | public bool IsStatic => (GetMethod ?? SetMethod!).IsStatic; 48 | 49 | public bool IsAbstract => (GetMethod ?? SetMethod!).IsAbstract; 50 | 51 | public bool IsVirtual => (GetMethod ?? SetMethod!).IsVirtual; 52 | 53 | private MetadataPropertyInfo(PropertyInfo prop) => UnderlyingProperty = prop; 54 | 55 | public static IPropertyInfo CreateFrom(PropertyInfo prop) => new MetadataPropertyInfo(prop); 56 | 57 | public override bool Equals(object obj) => obj is MetadataPropertyInfo that && UnderlyingProperty.Equals(that.UnderlyingProperty); 58 | 59 | public override int GetHashCode() => UnderlyingProperty.GetHashCode(); 60 | 61 | public override string ToString() => UnderlyingProperty.ToString(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SRC/Private/Reflection/SymbolEventInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SymbolEventInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Diagnostics; 7 | 8 | using Microsoft.CodeAnalysis; 9 | 10 | 11 | namespace Solti.Utils.Proxy.Internals 12 | { 13 | internal sealed class SymbolEventInfo : IEventInfo 14 | { 15 | private IEventSymbol UnderlyingSymbol { get; } 16 | 17 | private Compilation Compilation { get; } 18 | 19 | public string Name => UnderlyingSymbol.StrippedName(); 20 | 21 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 22 | private ITypeInfo? FDeclaringType; 23 | public ITypeInfo DeclaringType => FDeclaringType ??= (AddMethod ?? RemoveMethod!).DeclaringType; 24 | 25 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 26 | private IMethodInfo? FAddMethod; 27 | public IMethodInfo AddMethod => FAddMethod ??= SymbolMethodInfo.CreateFrom(UnderlyingSymbol.AddMethod!, Compilation); 28 | 29 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 30 | private IMethodInfo? FRemoveMethod; 31 | public IMethodInfo RemoveMethod => FRemoveMethod ??= SymbolMethodInfo.CreateFrom(UnderlyingSymbol.RemoveMethod!, Compilation); 32 | 33 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 34 | private ITypeInfo? FType; 35 | public ITypeInfo Type => FType ??= SymbolTypeInfo.CreateFrom(UnderlyingSymbol.Type, Compilation); 36 | 37 | public bool IsStatic => (AddMethod ?? RemoveMethod!).IsStatic; 38 | 39 | public bool IsAbstract => (AddMethod ?? RemoveMethod!).IsAbstract; 40 | 41 | public bool IsVirtual => (AddMethod ?? RemoveMethod!).IsVirtual; 42 | 43 | private SymbolEventInfo(IEventSymbol evt, Compilation compilation) 44 | { 45 | UnderlyingSymbol = evt; 46 | Compilation = compilation; 47 | } 48 | 49 | public static IEventInfo CreateFrom(IEventSymbol evt, Compilation compilation) 50 | { 51 | evt.EnsureNotError(); 52 | 53 | return new SymbolEventInfo(evt, compilation); 54 | } 55 | 56 | public override bool Equals(object obj) => obj is SymbolEventInfo that && SymbolEqualityComparer.Default.Equals(that.UnderlyingSymbol, UnderlyingSymbol); 57 | 58 | public override int GetHashCode() => SymbolEqualityComparer.Default.GetHashCode(UnderlyingSymbol); 59 | 60 | public override string ToString() => UnderlyingSymbol.ToString(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SRC/Private/Reflection/SymbolGenericConstraint.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SymbolGenericConstraint.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Collections.Generic; 7 | using System.Collections.Immutable; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | 11 | using Microsoft.CodeAnalysis; 12 | 13 | namespace Solti.Utils.Proxy.Internals 14 | { 15 | internal sealed class SymbolGenericConstraint(ITypeParameterSymbol genericArgument, Compilation compilation) : IGenericConstraint 16 | { 17 | public static IGenericConstraint? CreateFrom(ITypeParameterSymbol genericArgument, Compilation compilation) => 18 | !genericArgument.HasConstructorConstraint && !genericArgument.HasReferenceTypeConstraint && !genericArgument.HasValueTypeConstraint && !genericArgument.ConstraintTypes.Any() 19 | ? null 20 | : new SymbolGenericConstraint(genericArgument, compilation); 21 | 22 | public bool DefaultConstructor => genericArgument.HasConstructorConstraint; 23 | 24 | public bool Reference => genericArgument.HasReferenceTypeConstraint; 25 | 26 | public bool Struct => genericArgument.HasValueTypeConstraint; 27 | 28 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 29 | private IReadOnlyList? FConstraintTypes; 30 | public IReadOnlyList ConstraintTypes => FConstraintTypes ??= genericArgument 31 | .ConstraintTypes 32 | .Select(t => SymbolTypeInfo.CreateFrom(t, compilation)) 33 | .ToImmutableList(); 34 | 35 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 36 | private ITypeInfo? FTarget; 37 | public ITypeInfo Target => FTarget ??= SymbolTypeInfo.CreateFrom(genericArgument, compilation); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SRC/Private/Reflection/SymbolParameterInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SymbolParameterInfo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Diagnostics; 7 | 8 | using Microsoft.CodeAnalysis; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal sealed class SymbolParameterInfo : IParameterInfo 13 | { 14 | private IParameterSymbol UnderlyingSymbol { get; } 15 | 16 | private Compilation Compilation { get; } 17 | 18 | private SymbolParameterInfo(IParameterSymbol para, Compilation compilation) 19 | { 20 | UnderlyingSymbol = para; 21 | Compilation = compilation; 22 | } 23 | 24 | public static IParameterInfo CreateFrom(IParameterSymbol para, Compilation compilation) 25 | { 26 | para.EnsureNotError(); 27 | 28 | return new SymbolParameterInfo(para, compilation); 29 | } 30 | 31 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 32 | private ITypeInfo? FType; 33 | public ITypeInfo Type => FType ??= SymbolTypeInfo.CreateFrom(UnderlyingSymbol.Type, Compilation); 34 | 35 | public ParameterKind Kind => UnderlyingSymbol.GetParameterKind(); 36 | 37 | public string Name => UnderlyingSymbol.Name; 38 | 39 | public override bool Equals(object obj) => obj is SymbolParameterInfo that && SymbolEqualityComparer.Default.Equals(UnderlyingSymbol, that.UnderlyingSymbol); 40 | 41 | public override int GetHashCode() => SymbolEqualityComparer.Default.GetHashCode(UnderlyingSymbol); 42 | 43 | public override string ToString() => UnderlyingSymbol.ToString(); 44 | } 45 | } -------------------------------------------------------------------------------- /SRC/Private/Reflection/SymbolReturnParameterInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SymbolReturnParameterInfo.cs.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Diagnostics; 7 | 8 | using Microsoft.CodeAnalysis; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal sealed class SymbolReturnParameterInfo(IMethodSymbol method, Compilation compilation) : IParameterInfo 13 | { 14 | public ParameterKind Kind => method switch 15 | { 16 | { ReturnsByRefReadonly: true } => ParameterKind.RefReadonly, 17 | { ReturnsByRef: true } => ParameterKind.Ref, 18 | _ => ParameterKind.Out 19 | }; 20 | 21 | public string Name { get; } = string.Empty; 22 | 23 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 24 | private ITypeInfo? FType; 25 | public ITypeInfo Type => FType ??= SymbolTypeInfo.CreateFrom(method.ReturnType, compilation); 26 | 27 | public static IParameterInfo CreateFrom(IMethodSymbol method, Compilation compilation) 28 | { 29 | method.EnsureNotError(); 30 | 31 | return new SymbolReturnParameterInfo(method, compilation); 32 | } 33 | 34 | public override bool Equals(object obj) => obj is SymbolReturnParameterInfo that && that.Type.Equals(Type); 35 | 36 | public override int GetHashCode() => new { Type, Kind }.GetHashCode(); 37 | 38 | public override string ToString() => $"{Type}&"; 39 | } 40 | } -------------------------------------------------------------------------------- /SRC/Private/SourceCode.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SourceCode.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Text; 8 | 9 | using Microsoft.CodeAnalysis; 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | using Microsoft.CodeAnalysis.Text; 12 | 13 | namespace Solti.Utils.Proxy.Internals 14 | { 15 | internal sealed class SourceCode(string hint, CompilationUnitSyntax unit) 16 | { 17 | public string Hint { get; } = hint; 18 | 19 | public SourceText Value { get; } = SourceText.From 20 | ( 21 | unit.NormalizeWhitespace(eol: Environment.NewLine).ToFullString(), 22 | Encoding.UTF8 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SRC/Private/SourceGenerators/Diagnostics.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * Diagnostics.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using Microsoft.CodeAnalysis; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | using Properties; 11 | 12 | internal static class Diagnostics 13 | { 14 | public delegate Diagnostic DiagnosticFactory(Location location, params object[] args); 15 | 16 | public static DiagnosticFactory PGE00 { get; } = CreateFactory(nameof(PGE00), SGResources.LNG_NOT_SUPPORTED, SGResources.LNG_NOT_SUPPORTED, DiagnosticSeverity.Warning); 17 | 18 | public static DiagnosticFactory PGE01 { get; } = CreateFactory(nameof(PGE01), SGResources.TE_FAILED, SGResources.TE_FAILED_FULL, DiagnosticSeverity.Warning); 19 | 20 | public static DiagnosticFactory PGI00 { get; } = CreateFactory(nameof(PGI00), SGResources.SRC_EXTENDED, SGResources.SRC_EXTENDED_FULL, DiagnosticSeverity.Info); 21 | 22 | private static DiagnosticFactory CreateFactory(string id, string message, string messageFtm, DiagnosticSeverity severity) => (location, args) => Diagnostic.Create 23 | ( 24 | new DiagnosticDescriptor(id, message, string.Format(SGResources.Culture, messageFtm, args), SGResources.TE, severity, true), 25 | location 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SRC/Private/SourceGenerators/ProxyEmbedder.RoslynV3.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ProxyEmbedder.RoslynV3.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | #if LEGACY_COMPILER 7 | using System.Collections.Generic; 8 | 9 | using Microsoft.CodeAnalysis; 10 | 11 | namespace Solti.Utils.Proxy.Internals 12 | { 13 | using Attributes; 14 | 15 | #pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant 16 | [Generator(LanguageNames.CSharp)] 17 | #pragma warning restore CS3016 18 | internal sealed class ProxyEmbedder : ProxyEmbedderBase, ISourceGenerator 19 | { 20 | internal static IEnumerable GetAOTGenerators(Compilation compilation) 21 | { 22 | INamedTypeSymbol egta = compilation.GetTypeByMetadataName(typeof(EmbedGeneratedTypeAttribute).FullName)!; 23 | 24 | #pragma warning disable RS1024 // Compare symbols correctly 25 | HashSet returnedGenerators = new(SymbolEqualityComparer); 26 | #pragma warning restore RS1024 27 | 28 | foreach (AttributeData attr in compilation.Assembly.GetAttributes()) 29 | { 30 | if (!SymbolEqualityComparer.Equals(attr.AttributeClass, egta)) 31 | continue; 32 | 33 | if (attr.ConstructorArguments.Length is not 1 || attr.ConstructorArguments[0].Value is not INamedTypeSymbol generator) 34 | continue; 35 | 36 | if (returnedGenerators.Add(generator)) 37 | yield return generator; 38 | } 39 | } 40 | 41 | void ISourceGenerator.Initialize(GeneratorInitializationContext context) 42 | { 43 | } 44 | 45 | void ISourceGenerator.Execute(GeneratorExecutionContext context) => Execute 46 | ( 47 | context.Compilation, 48 | context 49 | .AnalyzerConfigOptions 50 | .GlobalOptions, 51 | new List 52 | ( 53 | GetAOTGenerators(context.Compilation) 54 | ), 55 | context.ReportDiagnostic, 56 | src => context.AddSource(src.Hint, src.Value), 57 | context.CancellationToken 58 | ); 59 | } 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/ClassProxySyntaxFactory.ConstructorFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ClassProxySyntaxFactory.ConstructorFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | using Microsoft.CodeAnalysis.CSharp.Syntax; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | using Properties; 13 | 14 | internal partial class ClassProxySyntaxFactory 15 | { 16 | #if DEBUG 17 | internal 18 | #endif 19 | protected override ClassDeclarationSyntax ResolveConstructors(ClassDeclarationSyntax cls, object context) 20 | { 21 | int found = 0; 22 | 23 | foreach (IConstructorInfo ctor in FBaseType.GetConstructors(AccessModifiers.Protected)) 24 | if (IsVisible(ctor)) 25 | { 26 | cls = ResolveConstructor(cls, context, ctor); 27 | found++; 28 | } 29 | 30 | if (found is 0) 31 | throw new InvalidOperationException(string.Format(Resources.NO_ACCESSIBLE_CTOR, FBaseType.Name)); 32 | 33 | return cls; 34 | } 35 | 36 | /// 37 | /// 38 | /// public MyClass(T param1, TT param2): base(param1, param2) {} 39 | /// 40 | /// 41 | #if DEBUG 42 | internal 43 | #endif 44 | protected override ClassDeclarationSyntax ResolveConstructor(ClassDeclarationSyntax cls, object context, IConstructorInfo ctor) => cls.AddMembers 45 | ( 46 | ResolveConstructor(ctor, cls.Identifier) 47 | ); 48 | } 49 | } -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/ClassSyntaxFactoryBase.Variable.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ClassSyntaxFactoryBase.Variable.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | 8 | using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal partial class ClassSyntaxFactoryBase 13 | { 14 | /// 15 | /// 16 | /// System.Object paramName [= ...]; 17 | /// 18 | /// 19 | #if DEBUG 20 | internal 21 | #endif 22 | protected LocalDeclarationStatementSyntax ResolveLocal(ITypeInfo type, string name, ExpressionSyntax? initializer = null) 23 | { 24 | VariableDeclaratorSyntax declarator = VariableDeclarator 25 | ( 26 | identifier: Identifier(name) 27 | ); 28 | 29 | if (initializer is not null) declarator = declarator.WithInitializer 30 | ( 31 | initializer: EqualsValueClause 32 | ( 33 | initializer 34 | ) 35 | ); 36 | 37 | return LocalDeclarationStatement 38 | ( 39 | declaration: VariableDeclaration 40 | ( 41 | type: ResolveType(type), 42 | variables: SingletonSeparatedList(declarator) 43 | ) 44 | ); 45 | } 46 | 47 | /// 48 | /// 49 | /// System.Object paramName [= ...]; 50 | /// 51 | /// 52 | #if DEBUG 53 | internal 54 | #endif 55 | protected LocalDeclarationStatementSyntax ResolveLocal(string name, ExpressionSyntax? initializer = null) => ResolveLocal(MetadataTypeInfo.CreateFrom(typeof(T)), name, initializer); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/DelegateProxySyntaxFactory.Wrapped.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DelegateProxySyntaxFactory.Wrapped.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Diagnostics; 7 | 8 | using Microsoft.CodeAnalysis.CSharp.Syntax; 9 | 10 | using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 11 | 12 | namespace Solti.Utils.Proxy.Internals 13 | { 14 | internal sealed partial class DelegateProxySyntaxFactory 15 | { 16 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 17 | private static readonly IPropertyInfo FWrapped = MetadataPropertyInfo.CreateFrom 18 | ( 19 | PropertyInfoExtensions.ExtractFrom(static (IDelegateWrapper w) => w.Wrapped) 20 | ); 21 | 22 | /// 23 | /// 24 | /// Delegate Wrapped => (TConcreteDelegate) Invoke; 25 | /// 26 | /// 27 | #if DEBUG 28 | internal 29 | #endif 30 | protected override ClassDeclarationSyntax ResolveProperties(ClassDeclarationSyntax cls, object context) => base.ResolveProperties 31 | ( 32 | cls.AddMembers 33 | ( 34 | ResolveProperty 35 | ( 36 | FWrapped, 37 | getBody: ArrowExpressionClause 38 | ( 39 | CastExpression 40 | ( 41 | ResolveType(TargetType!), 42 | SimpleMemberAccess 43 | ( 44 | ThisExpression(), 45 | INVOKE_METHOD_NAME 46 | ) 47 | ) 48 | ), 49 | setBody: null 50 | ) 51 | ), 52 | context 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/DelegateProxySyntaxFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DelegateProxySyntaxFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | using System.Threading; 11 | 12 | using Microsoft.CodeAnalysis.CSharp.Syntax; 13 | 14 | namespace Solti.Utils.Proxy.Internals 15 | { 16 | using Properties; 17 | 18 | internal sealed partial class DelegateProxySyntaxFactory : ProxyUnitSyntaxFactory 19 | { 20 | private const string INVOKE_METHOD_NAME = nameof(Action.Invoke); 21 | 22 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 23 | private readonly IMethodInfo FInvokeDelegate; 24 | 25 | #if DEBUG 26 | internal 27 | #endif 28 | protected override IEnumerable ResolveClasses(object context, CancellationToken cancellation) 29 | { 30 | yield return ResolveClass(context, cancellation); 31 | } 32 | 33 | public override string ExposedClass => $"DelegateProxy_{TargetType!.GetMD5HashCode()}"; 34 | 35 | #if DEBUG 36 | internal 37 | #endif 38 | protected override IReadOnlyList Bases => 39 | [ 40 | MetadataTypeInfo.CreateFrom(typeof(IDelegateWrapper)), 41 | ..base.Bases 42 | ]; 43 | 44 | public DelegateProxySyntaxFactory(ITypeInfo targetType, SyntaxFactoryContext context) : base(targetType, context) 45 | { 46 | if (!targetType.Flags.HasFlag(TypeInfoFlags.IsDelegate)) 47 | throw new ArgumentException(Resources.NOT_A_DELEGATE, nameof(targetType)); 48 | 49 | if (targetType is IGenericTypeInfo generic && generic.IsGenericDefinition) 50 | throw new ArgumentException(Resources.GENERIC_TARGET, nameof(targetType)); 51 | 52 | FInvokeDelegate = targetType.Methods.Single(static m => m.Name == INVOKE_METHOD_NAME); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/InterfaceProxySyntaxFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * InterfaceProxySyntaxFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Threading; 10 | 11 | using Microsoft.CodeAnalysis.CSharp.Syntax; 12 | 13 | namespace Solti.Utils.Proxy.Internals 14 | { 15 | using Properties; 16 | 17 | internal sealed partial class InterfaceProxySyntaxFactory: ProxyUnitSyntaxFactory 18 | { 19 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 20 | private static readonly IMethodInfo FGetImplementedInterfaceMethod = MetadataMethodInfo.CreateFrom 21 | ( 22 | MethodInfoExtensions.ExtractFrom(static output => CurrentMember.GetImplementedInterfaceMethod(ref output)) 23 | ); 24 | 25 | public override string ExposedClass => $"Proxy_{TargetType!.GetMD5HashCode()}"; 26 | 27 | #if DEBUG 28 | internal 29 | #endif 30 | protected override IReadOnlyList Bases => [TargetType!, ..base.Bases]; 31 | 32 | #if DEBUG 33 | internal 34 | #endif 35 | protected override IEnumerable ResolveClasses(object context, CancellationToken cancellation) 36 | { 37 | yield return ResolveClass(context, cancellation); 38 | } 39 | 40 | public InterfaceProxySyntaxFactory(ITypeInfo interfaceType, SyntaxFactoryContext context) : base(interfaceType, context) 41 | { 42 | if (!interfaceType.Flags.HasFlag(TypeInfoFlags.IsInterface)) 43 | throw new ArgumentException(Resources.NOT_AN_INTERFACE, nameof(interfaceType)); 44 | 45 | if (interfaceType is IGenericTypeInfo genericIface && genericIface.IsGenericDefinition) 46 | throw new ArgumentException(Resources.GENERIC_IFACE, nameof(interfaceType)); 47 | } 48 | 49 | #if DEBUG 50 | internal 51 | #endif 52 | protected override CompilationUnitSyntax ResolveUnitCore(object context, CancellationToken cancellation) 53 | { 54 | Visibility.Check(TargetType!, ContainingAssembly); 55 | 56 | return base.ResolveUnitCore(context, cancellation); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/ProxyUnitSyntaxFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ProxyUnitSyntaxFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy.Internals 7 | { 8 | internal abstract partial class ProxyUnitSyntaxFactory(ITypeInfo? targetType, SyntaxFactoryContext context) : ProxyUnitSyntaxFactoryBase(targetType, context) 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/ProxyUnitSyntaxFactoryBase.Constructor.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ProxyUnitSyntaxFactoryBase.Constructor.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Linq; 7 | 8 | using Microsoft.CodeAnalysis.CSharp.Syntax; 9 | 10 | namespace Solti.Utils.Proxy.Internals 11 | { 12 | internal partial class ProxyUnitSyntaxFactoryBase 13 | { 14 | #if DEBUG 15 | internal 16 | #endif 17 | protected override ClassDeclarationSyntax ResolveConstructors(ClassDeclarationSyntax cls, object context) => cls.AddMembers 18 | ( 19 | ResolveConstructor 20 | ( 21 | MetadataTypeInfo.CreateFrom(typeof(object)) 22 | .Constructors 23 | .Single(), 24 | cls.Identifier 25 | ) 26 | ); 27 | } 28 | } -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/ProxyUnitSyntaxFactoryBase.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ProxyUnitSyntaxFactoryBase.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | 12 | namespace Solti.Utils.Proxy.Internals 13 | { 14 | internal abstract partial class ProxyUnitSyntaxFactoryBase(ITypeInfo? targetType, SyntaxFactoryContext context) : UnitSyntaxFactoryBase(context) 15 | { 16 | public ITypeInfo? TargetType { get; } = targetType; 17 | 18 | #if DEBUG 19 | internal 20 | #endif 21 | protected override CompilationUnitSyntax ResolveUnitCore(object context, CancellationToken cancellation) 22 | { 23 | if (TargetType is not null) 24 | Visibility.Check(TargetType, ContainingAssembly); 25 | 26 | return base.ResolveUnitCore(context, cancellation); 27 | } 28 | 29 | #if DEBUG 30 | internal 31 | #endif 32 | protected override IEnumerable> MemberResolvers 33 | { 34 | get 35 | { 36 | foreach (Func resolver in base.MemberResolvers) 37 | { 38 | yield return resolver; 39 | } 40 | yield return ResolveActivator; 41 | yield return ResolveInitializer; 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/SyntaxFactoryBase.Attribute.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SyntaxFactoryBase.Attribute.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | 11 | using Microsoft.CodeAnalysis.CSharp.Syntax; 12 | 13 | using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 14 | 15 | namespace Solti.Utils.Proxy.Internals 16 | { 17 | internal partial class SyntaxFactoryBase 18 | { 19 | #if DEBUG 20 | internal 21 | #endif 22 | protected AttributeSyntax ResolveAttribute(Type attribute, params IEnumerable paramz) 23 | { 24 | Debug.Assert(typeof(Attribute).IsAssignableFrom(attribute)); 25 | 26 | AttributeSyntax attr = Attribute 27 | ( 28 | (NameSyntax) ResolveType(MetadataTypeInfo.CreateFrom(attribute)) 29 | ); 30 | 31 | if (paramz.Any()) attr = attr.WithArgumentList 32 | ( 33 | argumentList: AttributeArgumentList 34 | ( 35 | arguments: paramz.ToSyntaxList(AttributeArgument) 36 | ) 37 | ); 38 | 39 | return attr; 40 | } 41 | 42 | #if DEBUG 43 | internal 44 | #endif 45 | protected AttributeSyntax ResolveAttribute(params IEnumerable paramz) where TAttribute : Attribute => 46 | ResolveAttribute(typeof(TAttribute), paramz); 47 | } 48 | } -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactories/SyntaxFactoryBase.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SyntaxFactoryBase.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Diagnostics; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | internal abstract partial class SyntaxFactoryBase(SyntaxFactoryContext context) 11 | { 12 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 13 | private ILogger? FLogger; 14 | 15 | /// 16 | /// The context associated with this factory. 17 | /// 18 | public SyntaxFactoryContext Context { get; } = context; 19 | 20 | /// 21 | /// The class that is implemented by this factory. After compiling the code the caller can acquire the generated type using this property. 22 | /// 23 | public abstract string ExposedClass { get; } 24 | 25 | /// 26 | /// The assembly defined by this factory. 27 | /// 28 | public string ContainingAssembly => Context.AssemblyNameOverride ?? ExposedClass; 29 | 30 | /// 31 | /// The logger associated with this instance. Log scopes are created using the property 32 | /// 33 | public ILogger Logger => FLogger ??= Context.LoggerFactory.CreateLogger(ExposedClass); 34 | } 35 | } -------------------------------------------------------------------------------- /SRC/Private/SyntaxFactoryContext.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SyntaxFactoryContext.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using Microsoft.CodeAnalysis.CSharp; 7 | 8 | namespace Solti.Utils.Proxy.Internals 9 | { 10 | /// 11 | /// Common context used by syntax factories 12 | /// 13 | internal sealed record SyntaxFactoryContext 14 | { 15 | /// 16 | /// The output type. for source embedding and for module generation. 17 | /// 18 | public required OutputType OutputType { get; init; } 19 | 20 | /// 21 | /// The logger factory associated with this context. Concrete loggers can be instantiated by providing a log scope. 22 | /// 23 | public required ILoggerFactory LoggerFactory { get; init; } 24 | 25 | /// 26 | /// The reference collector instance or null if the source is being embedded. 27 | /// 28 | public IReferenceCollector? ReferenceCollector { get; init; } 29 | 30 | /// 31 | /// The name of assembly being compiled. Intended for test purposes 32 | /// 33 | public string? AssemblyNameOverride { get; init; } 34 | 35 | /// 36 | /// Language version to be used. 37 | /// 38 | public LanguageVersion LanguageVersion { get; init; } = LanguageVersion.CSharp9; 39 | 40 | /// 41 | /// The default configuration. Set up for module builds. 42 | /// 43 | public static SyntaxFactoryContext Default { get; } = new() 44 | { 45 | OutputType = OutputType.Module, 46 | 47 | // 48 | // We don't need to dispose the LoggerFactory as it always returns DebugLogger instances 49 | // 50 | 51 | LoggerFactory = new LoggerFactory(null) 52 | }; 53 | } 54 | } -------------------------------------------------------------------------------- /SRC/Properties/AssemblyAttributes.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * AssemblyAttributes.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Resources; 8 | using System.Runtime.CompilerServices; 9 | 10 | [ 11 | assembly: 12 | NeutralResourcesLanguage("en"), 13 | CLSCompliant(true), 14 | InternalsVisibleTo("ProxyGen.Perf") 15 | #if DEBUG 16 | , 17 | InternalsVisibleTo("Solti.Utils.Proxy.Tests") 18 | #endif 19 | ] 20 | -------------------------------------------------------------------------------- /SRC/ProxyGen.NET.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SRC/Public/Attributes/EmbedGeneratedTypeAttribute.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * EmbedGeneratedTypeAttribute.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Linq; 8 | 9 | namespace Solti.Utils.Proxy.Attributes 10 | { 11 | using Internals; 12 | using Properties; 13 | 14 | /// 15 | /// Instructs the system to embed the generated type into the annotated assembly. 16 | /// 17 | /// This feature is implemented with a source generator. 18 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 19 | public sealed class EmbedGeneratedTypeAttribute: Attribute 20 | { 21 | /// 22 | /// The related . 23 | /// 24 | public Type Generator { get; } 25 | 26 | /// 27 | /// Creates a new instance. 28 | /// 29 | public EmbedGeneratedTypeAttribute(Type generator) 30 | { 31 | if (generator is null) 32 | throw new ArgumentNullException(nameof(generator)); 33 | 34 | if (!generator.GetBaseTypes().Any(static @base => @base.IsConstructedGenericType && @base.GetGenericTypeDefinition().FullName == typeof(Generator<,>).FullName)) 35 | throw new ArgumentException(Resources.NOT_A_GENERATOR, nameof(generator)); 36 | 37 | Generator = generator; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SRC/Public/Generators/ClassProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ClassProxyGenerator.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | using Tuple = 11 | #if NETSTANDARD2_1_OR_GREATER 12 | System.Runtime.CompilerServices.ITuple; 13 | #else 14 | object; 15 | #endif 16 | 17 | namespace Solti.Utils.Proxy.Generators 18 | { 19 | using Internals; 20 | 21 | /// 22 | /// Type generator for creating proxies that intercept class method calls. 23 | /// 24 | public sealed class ClassProxyGenerator(Type @class) : Generator(id: GenerateId(nameof(ClassProxyGenerator), @class)) 25 | { 26 | /// 27 | /// The target class 28 | /// 29 | public Type Class { get; } = @class ?? throw new ArgumentNullException(nameof(@class)); 30 | 31 | /// 32 | /// Activates the proxy type. 33 | /// 34 | public async Task ActivateAsync(IInterceptor interceptor, Tuple ctorParamz, CancellationToken cancellation = default) 35 | { 36 | IInterceptorAccess interceptorAccess = (IInterceptorAccess) await ActivateAsync(ctorParamz, cancellation); 37 | interceptorAccess.Interceptor = interceptor; 38 | return interceptorAccess; 39 | } 40 | 41 | /// 42 | /// Activates the underlying proxy type. 43 | /// 44 | public object Activate(IInterceptor interceptor, Tuple ctorParamz) => ActivateAsync(interceptor, ctorParamz, CancellationToken.None) 45 | .GetAwaiter() 46 | .GetResult(); 47 | 48 | private protected override ProxyUnitSyntaxFactoryBase CreateMainUnit(SyntaxFactoryContext context) => new ClassProxySyntaxFactory 49 | ( 50 | MetadataTypeInfo.CreateFrom(Class), 51 | context 52 | ); 53 | } 54 | } -------------------------------------------------------------------------------- /SRC/Public/Generators/ClassProxyGenerator{TClass}.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ClassProxyGenerator{TClass}.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | using Microsoft.CodeAnalysis; 10 | using Microsoft.CodeAnalysis.CSharp; 11 | 12 | using Tuple = 13 | #if NETSTANDARD2_1_OR_GREATER 14 | System.Runtime.CompilerServices.ITuple; 15 | #else 16 | object; 17 | #endif 18 | 19 | namespace Solti.Utils.Proxy.Generators 20 | { 21 | using Internals; 22 | 23 | file sealed class SupportsSourceGenerationAttribute : SupportsSourceGenerationAttributeBase 24 | { 25 | public override ProxyUnitSyntaxFactoryBase CreateMainUnit(INamedTypeSymbol generator, CSharpCompilation compilation, SyntaxFactoryContext context) => new ClassProxySyntaxFactory 26 | ( 27 | SymbolTypeInfo.CreateFrom(generator.TypeArguments[0], compilation), 28 | context 29 | ); 30 | } 31 | 32 | /// 33 | /// Type generator for creating proxies that intercept class method calls. 34 | /// 35 | /// The class to be proxied 36 | [SupportsSourceGeneration] 37 | public sealed class ClassProxyGenerator : Generator> where TClass : class 38 | { 39 | /// 40 | protected override ClassProxyGenerator GetConcreteGenerator() => new(typeof(TClass)); 41 | 42 | /// 43 | /// Creates an instance of the generated type. 44 | /// 45 | public static async Task ActivateAsync(IInterceptor interceptor, Tuple ctorParamz, CancellationToken cancellation = default) => 46 | (TClass) await Instance.ActivateAsync(interceptor, ctorParamz, cancellation); 47 | 48 | /// 49 | /// Creates an instance of the generated type. 50 | /// 51 | public static TClass Activate(IInterceptor interceptor, Tuple ctorParamz) => 52 | (TClass) Instance.Activate(interceptor, ctorParamz); 53 | } 54 | } -------------------------------------------------------------------------------- /SRC/Public/Generators/DelegateProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DelegateProxyGenerator.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Solti.Utils.Proxy.Generators 11 | { 12 | using Internals; 13 | 14 | /// 15 | /// Type generator for creating proxies that intercept delegate invocations. 16 | /// 17 | public sealed class DelegateProxyGenerator(Type delegateType) : Generator(id: GenerateId(nameof(DelegateProxyGenerator), delegateType)) 18 | { 19 | /// 20 | /// The delegate type to be used. 21 | /// 22 | public Type DelegateType { get; } = delegateType ?? throw new ArgumentNullException(nameof(delegateType)); 23 | 24 | /// 25 | /// Activates the proxy type. 26 | /// 27 | public async Task ActivateAsync(IInterceptor interceptor, Delegate? @delegate, CancellationToken cancellation = default) 28 | { 29 | object result = await ActivateAsync(null, cancellation); 30 | 31 | ((IInterceptorAccess) result).Interceptor = interceptor; 32 | ((ITargetAccess) result).Target = @delegate; 33 | 34 | return ((IDelegateWrapper) result).Wrapped; 35 | } 36 | 37 | /// 38 | /// Activates the underlying proxy type. 39 | /// 40 | public object Activate(IInterceptor interceptor, Delegate? @delegate) => ActivateAsync(interceptor, @delegate, CancellationToken.None) 41 | .GetAwaiter() 42 | .GetResult(); 43 | 44 | private protected override ProxyUnitSyntaxFactoryBase CreateMainUnit(SyntaxFactoryContext context) => new DelegateProxySyntaxFactory 45 | ( 46 | MetadataTypeInfo.CreateFrom(DelegateType), 47 | context 48 | ); 49 | } 50 | } -------------------------------------------------------------------------------- /SRC/Public/Generators/DelegateProxyGenerator{TDelegate}.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DelegateProxyGenerator{TDelegate}.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | using Microsoft.CodeAnalysis; 11 | using Microsoft.CodeAnalysis.CSharp; 12 | 13 | namespace Solti.Utils.Proxy.Generators 14 | { 15 | using Internals; 16 | 17 | file sealed class SupportsSourceGenerationAttribute : SupportsSourceGenerationAttributeBase 18 | { 19 | public override ProxyUnitSyntaxFactoryBase CreateMainUnit(INamedTypeSymbol generator, CSharpCompilation compilation, SyntaxFactoryContext context) => new DelegateProxySyntaxFactory 20 | ( 21 | SymbolTypeInfo.CreateFrom(generator.TypeArguments[0], compilation), 22 | context 23 | ); 24 | } 25 | 26 | /// 27 | /// Type generator for creating proxies that intercept delegate invocations. 28 | /// 29 | /// The delegate to be proxied 30 | [SupportsSourceGeneration] 31 | public sealed class DelegateProxyGenerator : Generator> where TDelegate : Delegate 32 | { 33 | /// 34 | protected override DelegateProxyGenerator GetConcreteGenerator() => new(typeof(TDelegate)); 35 | 36 | /// 37 | /// Creates an instance of the generated type. 38 | /// 39 | public static async Task ActivateAsync(IInterceptor interceptor, TDelegate? @delegate, CancellationToken cancellation = default) => 40 | (TDelegate) await Instance.ActivateAsync(interceptor, @delegate, cancellation); 41 | 42 | /// 43 | /// Creates an instance of the generated type. 44 | /// 45 | public static TDelegate Activate(IInterceptor interceptor, TDelegate? @delegate) => 46 | (TDelegate) Instance.Activate(interceptor, @delegate); 47 | } 48 | } -------------------------------------------------------------------------------- /SRC/Public/Generators/DuckGenerator.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DuckGenerator.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Solti.Utils.Proxy.Generators 11 | { 12 | using Internals; 13 | 14 | /// 15 | /// Type generator for creating a proxy that wraps the to implement the . 16 | /// 17 | public sealed class DuckGenerator(Type iface, Type target) : Generator(id: GenerateId(nameof(DuckGenerator), iface, target)) 18 | { 19 | /// 20 | /// The target implementing all the members. 21 | /// 22 | public Type Target { get; } = target ?? throw new ArgumentNullException(nameof(target)); 23 | 24 | /// 25 | /// The interface for which the proxy will be created. 26 | /// 27 | public Type Interface { get; } = iface ?? throw new ArgumentNullException(nameof(iface)); 28 | 29 | /// 30 | /// Activates the underlying duck type. 31 | /// 32 | #if NETSTANDARD2_0 33 | new 34 | #endif 35 | public async Task ActivateAsync(object target, CancellationToken cancellation = default) 36 | { 37 | ITargetAccess targetAccess = (ITargetAccess) await base.ActivateAsync(null, cancellation); 38 | targetAccess.Target = target ?? throw new ArgumentNullException(nameof(target)); 39 | return targetAccess; 40 | } 41 | 42 | /// 43 | /// Activates the underlying duck type. 44 | /// 45 | public object Activate(object target) => ActivateAsync(target, CancellationToken.None).GetAwaiter().GetResult(); 46 | 47 | private protected override ProxyUnitSyntaxFactoryBase CreateMainUnit(SyntaxFactoryContext context) => new DuckSyntaxFactory 48 | ( 49 | MetadataTypeInfo.CreateFrom(Interface), 50 | MetadataTypeInfo.CreateFrom(Target), 51 | context 52 | ); 53 | } 54 | } -------------------------------------------------------------------------------- /SRC/Public/Generators/DuckGenerator{TInterface, TTarget}.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DuckGenerator{TInterface, TTarget}.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | using Microsoft.CodeAnalysis; 11 | using Microsoft.CodeAnalysis.CSharp; 12 | 13 | namespace Solti.Utils.Proxy.Generators 14 | { 15 | using Internals; 16 | 17 | file sealed class SupportsSourceGenerationAttribute : SupportsSourceGenerationAttributeBase 18 | { 19 | public override ProxyUnitSyntaxFactoryBase CreateMainUnit(INamedTypeSymbol generator, CSharpCompilation compilation, SyntaxFactoryContext context) => new DuckSyntaxFactory 20 | ( 21 | SymbolTypeInfo.CreateFrom(generator.TypeArguments[0], compilation), 22 | SymbolTypeInfo.CreateFrom(generator.TypeArguments[1], compilation), 23 | context 24 | ); 25 | } 26 | 27 | /// 28 | /// Type generator for creating a proxy that wraps the to implement the . 29 | /// 30 | /// The interface for which the proxy will be created. 31 | /// The target implementing all the members. 32 | [SupportsSourceGeneration] 33 | public sealed class DuckGenerator: Generator> where TInterface: class 34 | { 35 | /// 36 | protected override DuckGenerator GetConcreteGenerator() => new(typeof(TInterface), typeof(TTarget)); 37 | 38 | /// 39 | /// Creates an instance of the generated type. 40 | /// 41 | public static async Task ActivateAsync(TTarget target, CancellationToken cancellation = default) => 42 | (TInterface) await Instance.ActivateAsync(target ?? throw new ArgumentNullException(nameof(target)), cancellation); 43 | 44 | /// 45 | /// Creates an instance of the generated type. 46 | /// 47 | public static TInterface Activate(TTarget target) => 48 | (TInterface) Instance.Activate(target ?? throw new ArgumentNullException(nameof(target))); 49 | } 50 | } -------------------------------------------------------------------------------- /SRC/Public/Generators/InterfaceProxyGenerator.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * InterfaceProxyGenerator.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Solti.Utils.Proxy.Generators 11 | { 12 | using Internals; 13 | 14 | /// 15 | /// Creates a new instance which hooks into the given . 16 | /// 17 | /// The interface to be proxied 18 | public sealed class InterfaceProxyGenerator(Type @interface) : Generator(id: GenerateId(nameof(InterfaceProxyGenerator), @interface)) 19 | { 20 | /// 21 | /// The target class or interface for which the proxy will be created. 22 | /// 23 | public Type Interface { get; } = @interface ?? throw new ArgumentNullException(nameof(@interface)); 24 | 25 | /// 26 | /// Activates the proxy type. 27 | /// 28 | public async Task ActivateAsync(IInterceptor interceptor, object? target = null, CancellationToken cancellation = default) 29 | { 30 | object result = await base.ActivateAsync(null, cancellation); 31 | ((ITargetAccess) result).Target = target; 32 | ((IInterceptorAccess) result).Interceptor = interceptor ?? throw new ArgumentNullException(nameof(interceptor)); 33 | return result; 34 | } 35 | 36 | /// 37 | /// Activates the underlying duck type. 38 | /// 39 | public object Activate(IInterceptor interceptor, object? target = null) => ActivateAsync(interceptor, target, CancellationToken.None) 40 | .GetAwaiter() 41 | .GetResult(); 42 | 43 | private protected override ProxyUnitSyntaxFactoryBase CreateMainUnit(SyntaxFactoryContext context) => new InterfaceProxySyntaxFactory 44 | ( 45 | MetadataTypeInfo.CreateFrom(Interface), 46 | context 47 | ); 48 | } 49 | } -------------------------------------------------------------------------------- /SRC/Public/Generators/InterfaceProxyGenerator{TInterface}.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * InterfaceProxyGenerator{TInterface}.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | using Microsoft.CodeAnalysis; 10 | using Microsoft.CodeAnalysis.CSharp; 11 | 12 | namespace Solti.Utils.Proxy.Generators 13 | { 14 | using Internals; 15 | 16 | file sealed class SupportsSourceGenerationAttribute : SupportsSourceGenerationAttributeBase 17 | { 18 | public override ProxyUnitSyntaxFactoryBase CreateMainUnit(INamedTypeSymbol generator, CSharpCompilation compilation, SyntaxFactoryContext context) => new InterfaceProxySyntaxFactory 19 | ( 20 | SymbolTypeInfo.CreateFrom(generator.TypeArguments[0], compilation), 21 | context 22 | ); 23 | } 24 | 25 | /// 26 | /// Type generator for creating proxies that intercept interface method calls. 27 | /// 28 | /// The interface for which the proxy will be created. 29 | [SupportsSourceGeneration] 30 | public sealed class InterfaceProxyGenerator : Generator> where TInterface : class 31 | { 32 | /// 33 | protected override InterfaceProxyGenerator GetConcreteGenerator() => new(typeof(TInterface)); 34 | 35 | /// 36 | /// Creates an instance of the generated type. 37 | /// 38 | public static async Task ActivateAsync(IInterceptor interceptor, TInterface? target = null, CancellationToken cancellation = default) => 39 | (TInterface) await Instance.ActivateAsync(interceptor, target, cancellation); 40 | 41 | /// 42 | /// Creates an instance of the generated type. 43 | /// 44 | public static TInterface Activate(IInterceptor interceptor, TInterface? target = null) => 45 | (TInterface) Instance.Activate(interceptor, target); 46 | } 47 | } -------------------------------------------------------------------------------- /SRC/Public/Interfaces/IInterceptor.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IInterceptor.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | namespace Solti.Utils.Proxy 7 | { 8 | /// 9 | /// Describes an abstract interceptor 10 | /// 11 | public interface IInterceptor 12 | { 13 | /// 14 | /// Method to be called on proxy invocation. 15 | /// 16 | object? Invoke(IInvocationContext context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SRC/Public/Interfaces/IInvocationContext.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IInvocationContext.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Diagnostics.CodeAnalysis; 9 | 10 | namespace Solti.Utils.Proxy 11 | { 12 | /// 13 | /// Abstract invocation context 14 | /// 15 | public interface IInvocationContext 16 | { 17 | /// 18 | /// The proxy object on which the call was made. 19 | /// 20 | object Proxy { get; } 21 | 22 | /// 23 | /// The member (property, event or method) that is being proxied. 24 | /// 25 | ExtendedMemberInfo Member { get; } 26 | 27 | /// 28 | /// Generic arguments supplied by the caller. 29 | /// 30 | IReadOnlyList GenericArguments { get; } 31 | 32 | /// 33 | /// The arguments passed by the caller. 34 | /// 35 | /// Before the target gets called you may use this property to inspect or modify parameters passed by the caller. After it you can read or amend the "by ref" parameters set by the target method. 36 | [SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "End user is allowed to modify the argument list.")] 37 | object?[] Args { get; } 38 | 39 | /// 40 | /// Dispatches the current call to the target. 41 | /// 42 | object? Dispatch(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SRC/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt: -------------------------------------------------------------------------------- 1 | #nullable enable -------------------------------------------------------------------------------- /SRC/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | -------------------------------------------------------------------------------- /SRC/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [{ 3 | "src": [{ 4 | "src": "../BIN/Debug/ROSLYN_LATEST/netstandard2.1", 5 | "files": [ "*.dll" ] 6 | }], 7 | "dest": "../BIN/DOC", 8 | "properties": { 9 | "TargetFramework": "netstandard2.1" 10 | } 11 | }], 12 | "build": { 13 | "content": [ 14 | { 15 | "files": ["*.yml"], 16 | "src": "../BIN/DOC" 17 | }, 18 | { 19 | "files": ["index.md"] 20 | } 21 | ], 22 | "dest": "../Artifacts/DOC", 23 | "resource": [{ 24 | "files": ["icon.png"], 25 | "src": "../" 26 | }], 27 | "globalMetadata": { 28 | "_appTitle": "ProxyGen", 29 | "_appFaviconPath": "icon.png", 30 | "_disableNavbar": true, 31 | "_disableContribution": true 32 | }, 33 | "xrefService": [ 34 | "https://xref.docs.microsoft.com/query?uid={uid}" 35 | ] 36 | } 37 | } -------------------------------------------------------------------------------- /SRC/index.md: -------------------------------------------------------------------------------- 1 | # ProxyGen 2 | 3 | You can find the docs separated by their namepsaces: 4 | - [Solti.Utils.Proxy](https://sholtee.github.io/proxygen/doc/Solti.Utils.Proxy.html ) 5 | - [Solti.Utils.Proxy.Attributes](https://sholtee.github.io/proxygen/doc/Solti.Utils.Proxy.Attributes.html ) 6 | - [Solti.Utils.Proxy.Generators](https://sholtee.github.io/proxygen/doc/Solti.Utils.Proxy.Generators.html ) 7 | - [Solti.Utils.Proxy.Internals](https://sholtee.github.io/proxygen/doc/Solti.Utils.Proxy.Internals.html ) -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests.EmbeddedTypes/AssemblyAttributes.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * AssemblyAttributes.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections; 8 | using System.Collections.Generic; 9 | 10 | using Solti.Utils.Proxy.Attributes; 11 | using Solti.Utils.Proxy.Generators; 12 | using Solti.Utils.Proxy.Tests.EmbeddedTypes; 13 | [ 14 | assembly: 15 | EmbedGeneratedType(typeof(InterfaceProxyGenerator)), 16 | EmbedGeneratedType(typeof(InterfaceProxyGenerator>)), 17 | EmbedGeneratedType(typeof(InterfaceProxyGenerator)), 18 | EmbedGeneratedType(typeof(InterfaceProxyGenerator)), 19 | EmbedGeneratedType(typeof(DuckGenerator)), 20 | EmbedGeneratedType(typeof(DuckGenerator, List>)), 21 | EmbedGeneratedType(typeof(ClassProxyGenerator>)), 22 | EmbedGeneratedType(typeof(ClassProxyGenerator>)), 23 | EmbedGeneratedType(typeof(DelegateProxyGenerator>)), 24 | EmbedGeneratedType(typeof(DelegateProxyGenerator>)), 25 | EmbedGeneratedType(typeof(DelegateProxyGenerator>)) 26 | ] 27 | 28 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests.EmbeddedTypes/EmbeddedTypeExposer.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * EmbeddedTypeExposer.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Reflection; 8 | 9 | namespace Solti.Utils.Proxy.Tests.EmbeddedTypes 10 | { 11 | public static class EmbeddedTypeExposer 12 | { 13 | // 14 | // Nested types are accessible within the declaring assembly only 15 | // 16 | 17 | public static Type GetGeneratedTypeByGenerator() where TGenerator: new() => (Type) typeof(TGenerator).InvokeMember 18 | ( 19 | "GetGeneratedType", 20 | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod, 21 | null, 22 | null, 23 | new object[] { } 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests.EmbeddedTypes/IGenericInterfaceHavingConstraint.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IGenericInterfaceHavingConstraint.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | 7 | using System; 8 | 9 | namespace Solti.Utils.Proxy.Tests.EmbeddedTypes 10 | { 11 | internal interface IGenericInterfaceHavingConstraint 12 | { 13 | void Foo() where T : class, IDisposable; 14 | void Bar() where T : new() where TT : struct; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests.EmbeddedTypes/IInternalInterface.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * IInternalInterface.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | 7 | namespace Solti.Utils.Proxy.Tests.EmbeddedTypes 8 | { 9 | internal interface IInternalInterface 10 | { 11 | void Foo(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests.EmbeddedTypes/InternalDelegate.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * InternalDelegate.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | 7 | namespace Solti.Utils.Proxy.Tests.EmbeddedTypes 8 | { 9 | internal delegate int InternalDelegate(string a, ref T[] b, out object c); 10 | } 11 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests.EmbeddedTypes/InternalFoo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * InternalFoo.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | namespace Solti.Utils.Proxy.Tests.EmbeddedTypes 9 | { 10 | internal abstract class InternalFoo 11 | { 12 | public virtual T Prop { get; protected set; } = default!; 13 | public abstract T this[int i] { get; protected set; } 14 | public abstract event Action Event; 15 | public virtual T Bar(ref T param1, TT param2) => param1; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests.EmbeddedTypes/ProxyGen.Tests.EmbeddedTypes.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | Library 6 | false 7 | ..\..\BIN\$(Configuration)\$(Variant) 8 | Solti.Utils.Proxy.Tests.EmbeddedTypes 9 | Solti.Utils.Proxy.Tests.EmbeddedTypes 10 | 9 11 | enable 12 | true 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | $(OutputPath)Logs 25 | false 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/BarSrc.txt: -------------------------------------------------------------------------------- 1 | void global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.IFoo.Bar() 2 | { 3 | global::Solti.Utils.Proxy.Internals.CurrentMember.GetImplementedInterfaceMethod(ref global::Dummy.F3B606FE3C22242AFF40CD772EDD0F7D4); 4 | global::System.Object[] args = new global::System.Object[0]; 5 | ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, global::Dummy.F3B606FE3C22242AFF40CD772EDD0F7D4, (global::System.Object[] args) => 6 | { 7 | (this.FTarget ?? throw new global::System.InvalidOperationException()).Bar(); 8 | return null; 9 | }, args, new global::System.Type[0])); 10 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/Compile.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * Compile.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.CSharp; 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | 12 | using Moq; 13 | using NUnit.Framework; 14 | 15 | using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 16 | 17 | namespace Solti.Utils.Proxy.Internals.Tests 18 | { 19 | [TestFixture] 20 | public class CompileTests 21 | { 22 | [Test] 23 | public void ToAssembly_ShouldThrowIfTheCompilationFailed() 24 | { 25 | CompilationUnitSyntax unit = CompilationUnit().WithUsings 26 | ( 27 | usings: SingletonList 28 | ( 29 | UsingDirective 30 | ( 31 | name: IdentifierName("bad") 32 | ) 33 | ) 34 | ); 35 | 36 | Exception ex = Assert.Throws(() => Compile.ToAssembly(new CompilationUnitSyntax[] { unit }, "cica", null, Array.Empty(), LanguageVersion.Latest, new Mock().Object)); 37 | 38 | Assert.That(ex.Data["src"], Is.EqualTo("using bad;")); 39 | Assert.That(ex.Data["failures"], Is.Not.Empty); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/Delegate.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * Delegate.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | using NUnit.Framework; 10 | 11 | namespace Solti.Utils.Proxy.Internals.Tests 12 | { 13 | [TestFixture] 14 | public class DelegateTests 15 | { 16 | private static Func GetLambda(IList lst) => () => lst[0]; 17 | 18 | [Test] 19 | public void UnderlyingMethodOfDelegate_ShouldBeIndependentFromTheEnclosedVariables() 20 | { 21 | Assert.AreSame(GetLambda(new List()).Method, GetLambda(null).Method); 22 | Assert.AreSame(GetLambda(new List()).Method, GetLambda(new List()).Method); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/Duck_E40AA6C9C0242588555A55F2A4533DAB.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sholtee/proxygen/0ecd6213b3d73a567d5d65f6f7c632051ee8343c/TEST/ProxyGen.Tests/Duck_E40AA6C9C0242588555A55F2A4533DAB.dll -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/EventSrc.txt: -------------------------------------------------------------------------------- 1 | event global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.TestDelegate global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.IFoo.Event 2 | { 3 | add 4 | { 5 | global::Solti.Utils.Proxy.Internals.CurrentMember.GetImplementedInterfaceMethod(ref global::Dummy.F8AFB6CA98CF89121422C106996C9C75B); 6 | global::System.Object[] args = new global::System.Object[] 7 | { 8 | value 9 | }; 10 | ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, global::Dummy.F8AFB6CA98CF89121422C106996C9C75B, (global::System.Object[] args) => 11 | { 12 | global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.TestDelegate _value = (global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.TestDelegate)args[0]; 13 | (this.FTarget ?? throw new global::System.InvalidOperationException()).Event += _value; 14 | return null; 15 | }, args, new global::System.Type[0])); 16 | } 17 | 18 | remove 19 | { 20 | global::Solti.Utils.Proxy.Internals.CurrentMember.GetImplementedInterfaceMethod(ref global::Dummy.F75E8723887FE87A170AE953C4D35B5B4); 21 | global::System.Object[] args = new global::System.Object[] 22 | { 23 | value 24 | }; 25 | ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, global::Dummy.F75E8723887FE87A170AE953C4D35B5B4, (global::System.Object[] args) => 26 | { 27 | global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.TestDelegate _value = (global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.TestDelegate)args[0]; 28 | (this.FTarget ?? throw new global::System.InvalidOperationException()).Event -= _value; 29 | return null; 30 | }, args, new global::System.Type[0])); 31 | } 32 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/FooSrc.txt: -------------------------------------------------------------------------------- 1 | global::System.Int32 global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.IFoo.Foo(global::System.Int32 a, out global::System.String b, ref TT c) 2 | { 3 | global::Solti.Utils.Proxy.Internals.CurrentMember.GetImplementedInterfaceMethod(ref global::Dummy.F6DED5524BB02EB5ED49F044A93A795D6); 4 | global::System.Object[] args = new global::System.Object[] 5 | { 6 | a, 7 | default(global::System.String), 8 | c 9 | }; 10 | global::System.Object result = ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, global::Dummy.F6DED5524BB02EB5ED49F044A93A795D6, (global::System.Object[] args) => 11 | { 12 | global::System.Int32 _a = (global::System.Int32)args[0]; 13 | global::System.String _b; 14 | TT _c = (TT)args[2]; 15 | global::System.Object result = (this.FTarget ?? throw new global::System.InvalidOperationException()).Foo(_a, out _b, ref _c); 16 | args[1] = (global::System.Object)_b; 17 | args[2] = (global::System.Object)_c; 18 | return result; 19 | }, args, new global::System.Type[] { typeof(TT) })); 20 | b = (global::System.String)args[1]; 21 | c = (TT)args[2]; 22 | return (global::System.Int32)result; 23 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/FuncProxySrcModule.txt: -------------------------------------------------------------------------------- 1 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("ProxyGen.NET", "{version}"), global::System.Diagnostics.DebuggerNonUserCodeAttribute, global::System.Runtime.CompilerServices.CompilerGeneratedAttribute] 2 | internal sealed class DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4 : global::Solti.Utils.Proxy.Internals.IDelegateWrapper, global::Solti.Utils.Proxy.Internals.IInterceptorAccess, global::Solti.Utils.Proxy.Internals.ITargetAccess 3 | { 4 | private global::System.Func, global::System.Int32> FTarget; 5 | public DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4() : base() 6 | { 7 | } 8 | 9 | private global::System.Int32 Invoke(global::System.Collections.Generic.List arg) 10 | { 11 | global::System.Object[] args = new global::System.Object[] 12 | { 13 | arg 14 | }; 15 | global::System.Object result = ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, new global::Solti.Utils.Proxy.ExtendedMemberInfo((this.FTarget ?? throw new global::System.InvalidOperationException()).Method), (global::System.Object[] args) => 16 | { 17 | global::System.Collections.Generic.List _arg = (global::System.Collections.Generic.List)args[0]; 18 | global::System.Object result = (this.FTarget ?? throw new global::System.InvalidOperationException()).Invoke(_arg); 19 | return result; 20 | }, args, new global::System.Type[0])); 21 | return (global::System.Int32)result; 22 | } 23 | 24 | global::System.Delegate global::Solti.Utils.Proxy.Internals.IDelegateWrapper.Wrapped { get => (global::System.Func, global::System.Int32>)this.Invoke; } 25 | 26 | global::Solti.Utils.Proxy.IInterceptor global::Solti.Utils.Proxy.Internals.IInterceptorAccess.Interceptor { get; set; } 27 | 28 | global::System.Object global::Solti.Utils.Proxy.Internals.ITargetAccess.Target { get => this.FTarget; set => this.FTarget = (global::System.Func, global::System.Int32>)value; } 29 | 30 | public static readonly global::System.Func __Activator = static tuple => 31 | { 32 | switch (tuple) 33 | { 34 | case null: 35 | return new global::DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4(); 36 | default: 37 | throw new global::System.MissingMethodException("Constructor with the given layout cannot be found."); 38 | } 39 | }; 40 | [global::System.Runtime.CompilerServices.ModuleInitializerAttribute] 41 | public static void Initialize() => global::Solti.Utils.Proxy.Internals.LoadedTypes.Register(typeof(global::DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4), global::DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4.__Activator); 42 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/FuncProxySrcUnit.txt: -------------------------------------------------------------------------------- 1 | #pragma warning disable 2 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("ProxyGen.NET", "{version}"), global::System.Diagnostics.DebuggerNonUserCodeAttribute, global::System.Runtime.CompilerServices.CompilerGeneratedAttribute] 3 | internal sealed class DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4 : global::Solti.Utils.Proxy.Internals.IDelegateWrapper, global::Solti.Utils.Proxy.Internals.IInterceptorAccess, global::Solti.Utils.Proxy.Internals.ITargetAccess 4 | { 5 | private global::System.Func, global::System.Int32> FTarget; 6 | public DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4() : base() 7 | { 8 | } 9 | 10 | private global::System.Int32 Invoke(global::System.Collections.Generic.List arg) 11 | { 12 | global::System.Object[] args = new global::System.Object[] 13 | { 14 | arg 15 | }; 16 | global::System.Object result = ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, new global::Solti.Utils.Proxy.ExtendedMemberInfo((this.FTarget ?? throw new global::System.InvalidOperationException()).Method), (global::System.Object[] args) => 17 | { 18 | global::System.Collections.Generic.List _arg = (global::System.Collections.Generic.List)args[0]; 19 | global::System.Object result = (this.FTarget ?? throw new global::System.InvalidOperationException()).Invoke(_arg); 20 | return result; 21 | }, args, new global::System.Type[0])); 22 | return (global::System.Int32)result; 23 | } 24 | 25 | global::System.Delegate global::Solti.Utils.Proxy.Internals.IDelegateWrapper.Wrapped { get => (global::System.Func, global::System.Int32>)this.Invoke; } 26 | 27 | global::Solti.Utils.Proxy.IInterceptor global::Solti.Utils.Proxy.Internals.IInterceptorAccess.Interceptor { get; set; } 28 | 29 | global::System.Object global::Solti.Utils.Proxy.Internals.ITargetAccess.Target { get => this.FTarget; set => this.FTarget = (global::System.Func, global::System.Int32>)value; } 30 | 31 | public static readonly global::System.Func __Activator = static tuple => 32 | { 33 | switch (tuple) 34 | { 35 | case null: 36 | return new global::DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4(); 37 | default: 38 | throw new global::System.MissingMethodException("Constructor with the given layout cannot be found."); 39 | } 40 | }; 41 | [global::System.Runtime.CompilerServices.ModuleInitializerAttribute] 42 | public static void Initialize() => global::Solti.Utils.Proxy.Internals.LoadedTypes.Register(typeof(global::DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4), global::DelegateProxy_2F9A6934DF4602A77AF8367A6D7A92F4.__Activator); 43 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/IndexerSrc.txt: -------------------------------------------------------------------------------- 1 | global::System.Int32 global::System.Collections.Generic.IList.this[global::System.Int32 index] 2 | { 3 | get 4 | { 5 | global::Solti.Utils.Proxy.Internals.CurrentMember.GetImplementedInterfaceMethod(ref global::Dummy.F928810E559CA247F6317B6154D26128C); 6 | global::System.Object[] args = new global::System.Object[] 7 | { 8 | index 9 | }; 10 | global::System.Object result = ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, global::Dummy.F928810E559CA247F6317B6154D26128C, (global::System.Object[] args) => 11 | { 12 | global::System.Int32 _index = (global::System.Int32)args[0]; 13 | global::System.Object result = (this.FTarget ?? throw new global::System.InvalidOperationException())[_index]; 14 | return result; 15 | }, args, new global::System.Type[0])); 16 | return (global::System.Int32)result; 17 | } 18 | 19 | set 20 | { 21 | global::Solti.Utils.Proxy.Internals.CurrentMember.GetImplementedInterfaceMethod(ref global::Dummy.FC34DD35E83A0839578967C405AAE20D7); 22 | global::System.Object[] args = new global::System.Object[] 23 | { 24 | index, 25 | value 26 | }; 27 | ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, global::Dummy.FC34DD35E83A0839578967C405AAE20D7, (global::System.Object[] args) => 28 | { 29 | global::System.Int32 _index = (global::System.Int32)args[0]; 30 | global::System.Int32 _value = (global::System.Int32)args[1]; 31 | (this.FTarget ?? throw new global::System.InvalidOperationException())[_index] = _value; 32 | return null; 33 | }, args, new global::System.Type[0])); 34 | } 35 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/MemberInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * MemberInfoExtensions.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Reflection; 9 | 10 | using NUnit.Framework; 11 | 12 | namespace Solti.Utils.Proxy.Internals.Tests 13 | { 14 | [TestFixture] 15 | public class MemberInfoExtensionsTests 16 | { 17 | public static (object Member, bool IsStatic)[] StaticNonStatic = new[] 18 | { 19 | ((object) MetadataPropertyInfo.CreateFrom(typeof(AppDomain).GetProperty(nameof(AppDomain.CurrentDomain), BindingFlags.Static | BindingFlags.Public)), true), 20 | (MetadataMethodInfo.CreateFrom(typeof(AppDomain).GetMethod(nameof(AppDomain.GetCurrentThreadId), BindingFlags.Static | BindingFlags.Public)), true), 21 | (MetadataPropertyInfo.CreateFrom(typeof(List<>).GetProperty(nameof(List.Count))), false), 22 | (MetadataPropertyInfo.CreateFrom(typeof(List).GetProperty(nameof(List.Count))), false) 23 | }; 24 | 25 | [TestCaseSource(nameof(StaticNonStatic))] 26 | public void IsStatic_ShouldDoWhatTheNameSuggests((object Member, bool IsStatic) param) 27 | => Assert.That(((IMemberInfo) param.Member).IsStatic, Is.EqualTo(param.IsStatic)); 28 | 29 | private interface IFoo 30 | { 31 | int Method(ref string x); 32 | string Prop { get; set; } 33 | } 34 | 35 | private class SelectAttirubte: Attribute{} 36 | 37 | private class Foo : IFoo 38 | { 39 | [SelectAttirubte] 40 | string IFoo.Prop { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } 41 | [SelectAttirubte] 42 | int IFoo.Method(ref string x) => throw new NotImplementedException(); 43 | [SelectAttirubte] 44 | public string Prop { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } 45 | [SelectAttirubte] 46 | public int Method(ref string x) => throw new NotImplementedException(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/ModuleInitializerAttribute.txt: -------------------------------------------------------------------------------- 1 | #pragma warning disable 2 | #if !NET5_0_OR_GREATER 3 | namespace System.Runtime.CompilerServices 4 | { 5 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("ProxyGen.NET", "{version}"), global::System.Diagnostics.DebuggerNonUserCodeAttribute, global::System.Runtime.CompilerServices.CompilerGeneratedAttribute] 6 | internal sealed class ModuleInitializerAttribute : global::System.Attribute 7 | { 8 | } 9 | } 10 | #endif 11 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/PropSrc.txt: -------------------------------------------------------------------------------- 1 | global::System.Int32 global::Solti.Utils.Proxy.SyntaxFactories.Tests.SyntaxFactoryTestsBase.IFoo.Prop 2 | { 3 | get 4 | { 5 | global::Solti.Utils.Proxy.Internals.CurrentMember.GetImplementedInterfaceMethod(ref global::Dummy.F1666AD268EDC7839CF072E87227E9335); 6 | global::System.Object[] args = new global::System.Object[0]; 7 | global::System.Object result = ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, global::Dummy.F1666AD268EDC7839CF072E87227E9335, (global::System.Object[] args) => 8 | { 9 | global::System.Object result = (this.FTarget ?? throw new global::System.InvalidOperationException()).Prop; 10 | return result; 11 | }, args, new global::System.Type[0])); 12 | return (global::System.Int32)result; 13 | } 14 | 15 | set 16 | { 17 | global::Solti.Utils.Proxy.Internals.CurrentMember.GetImplementedInterfaceMethod(ref global::Dummy.F1481120FC9DA1A152C56B72C6EC8CFB6); 18 | global::System.Object[] args = new global::System.Object[] 19 | { 20 | value 21 | }; 22 | ((global::Solti.Utils.Proxy.Internals.IInterceptorAccess)this).Interceptor.Invoke(new global::Solti.Utils.Proxy.Internals.InvocationContext(this, global::Dummy.F1481120FC9DA1A152C56B72C6EC8CFB6, (global::System.Object[] args) => 23 | { 24 | global::System.Int32 _value = (global::System.Int32)args[0]; 25 | (this.FTarget ?? throw new global::System.InvalidOperationException()).Prop = _value; 26 | return null; 27 | }, args, new global::System.Type[0])); 28 | } 29 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/Proxy_24AA6ACBF0DFAE64A588818FEE12BF26.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sholtee/proxygen/0ecd6213b3d73a567d5d65f6f7c632051ee8343c/TEST/ProxyGen.Tests/Proxy_24AA6ACBF0DFAE64A588818FEE12BF26.dll -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/RandomInterfaces.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * RandomInterfaces.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | 10 | namespace Solti.Utils.Proxy.Tests 11 | { 12 | internal static class RandomInterfaces 13 | { 14 | public static IEnumerable Values 15 | { 16 | get 17 | { 18 | foreach 19 | ( 20 | Type iface in typeof(object) 21 | .Assembly 22 | .GetExportedTypes() 23 | .Where(t => t.IsInterface) 24 | ) 25 | { 26 | Type result = iface; 27 | 28 | if (result.ContainsGenericParameters) 29 | { 30 | Type[] gas = result.GetGenericArguments(); 31 | 32 | if (gas.Any(ga => ga.GetGenericParameterConstraints().Any())) 33 | continue; 34 | #if NET8_0_OR_GREATER 35 | try 36 | #endif 37 | { 38 | result = result.MakeGenericType(Enumerable.Repeat(typeof(T), gas.Length).ToArray()); 39 | } 40 | #if NET8_0_OR_GREATER 41 | // 42 | // GetGenericParameterConstraints() under .NET8.0 returns an empty array for some types having 43 | // self-referencing constraint (IClass where T: IClass). Seems a .NET bug... 44 | // 45 | 46 | catch (ArgumentException) 47 | { 48 | continue; 49 | } 50 | #endif 51 | } 52 | 53 | yield return result; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/SyntaxFactories/ClassProxySyntaxFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ClassProxySyntaxFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.IO; 8 | 9 | using Microsoft.CodeAnalysis; 10 | using NUnit.Framework; 11 | 12 | namespace Solti.Utils.Proxy.SyntaxFactories.Tests 13 | { 14 | using Internals; 15 | 16 | [TestFixture, Parallelizable(ParallelScope.All)] 17 | #if LEGACY_COMPILER 18 | [Ignore("Roslyn v3 has different formatting rules than v4")] 19 | #endif 20 | public sealed class ClassProxySyntaxFactoryTests : SyntaxFactoryTestsBase 21 | { 22 | private static ClassProxySyntaxFactory CreateSyntaxFactory(Type target, OutputType outputType) => new 23 | ( 24 | MetadataTypeInfo.CreateFrom(target), 25 | SyntaxFactoryContext.Default with 26 | { 27 | OutputType = outputType, 28 | AssemblyNameOverride = typeof(ClassProxySyntaxFactoryTests).Assembly.GetName().Name 29 | } 30 | ); 31 | 32 | [TestCase(typeof(Foo), OutputType.Module, "ClsProxySrcModule.txt")] 33 | [TestCase(typeof(Foo), OutputType.Unit, "ClsProxySrcUnit.txt")] 34 | public void ResolveUnit_ShouldGenerateTheDesiredUnit(Type target, int outputType, string fileName) 35 | { 36 | ClassProxySyntaxFactory gen = CreateSyntaxFactory(target, (OutputType) outputType); 37 | 38 | Assert.That 39 | ( 40 | gen 41 | .ResolveUnit(null, default) 42 | .NormalizeWhitespace(eol: "\n") 43 | .ToFullString(), 44 | Is.EqualTo 45 | ( 46 | File 47 | .ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName)) 48 | .Replace("\r", string.Empty) 49 | .Replace("{version}", typeof(ClassProxySyntaxFactory) 50 | .Assembly 51 | .GetName() 52 | .Version 53 | .ToString()) 54 | ) 55 | ); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/SyntaxFactories/DelegateProxySyntaxFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * DelegateProxySyntaxFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | 10 | using Microsoft.CodeAnalysis; 11 | using NUnit.Framework; 12 | 13 | namespace Solti.Utils.Proxy.SyntaxFactories.Tests 14 | { 15 | using Internals; 16 | 17 | [TestFixture, Parallelizable(ParallelScope.All)] 18 | #if LEGACY_COMPILER 19 | [Ignore("Roslyn v3 has different formatting rules than v4")] 20 | #endif 21 | public sealed class DelegateProxySyntaxFactoryTests 22 | { 23 | public delegate int MyDelegate(string a, ref T[] b, out object c); 24 | 25 | private static DelegateProxySyntaxFactory CreateSyntaxFactory(Type target, OutputType outputType) => new 26 | ( 27 | MetadataTypeInfo.CreateFrom(target), 28 | SyntaxFactoryContext.Default with 29 | { 30 | AssemblyNameOverride = typeof(DelegateProxySyntaxFactory).Assembly.GetName().Name, 31 | OutputType = outputType 32 | } 33 | ); 34 | 35 | [TestCase(typeof(Func, int>), OutputType.Module, "FuncProxySrcModule.txt")] 36 | [TestCase(typeof(Func, int>), OutputType.Unit, "FuncProxySrcUnit.txt")] 37 | [TestCase(typeof(MyDelegate>), OutputType.Module, "DelegateProxySrcModule.txt")] 38 | [TestCase(typeof(MyDelegate>), OutputType.Unit, "DelegateProxySrcUnit.txt")] 39 | public void ResolveUnit_ShouldGenerateTheDesiredUnit(Type target, int outputType, string fileName) 40 | { 41 | DelegateProxySyntaxFactory gen = CreateSyntaxFactory(target, (OutputType) outputType); 42 | 43 | Assert.That 44 | ( 45 | gen 46 | .ResolveUnit(null, default) 47 | .NormalizeWhitespace(eol: "\n") 48 | .ToFullString(), 49 | Is.EqualTo 50 | ( 51 | File 52 | .ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName)) 53 | .Replace("\r", string.Empty) 54 | .Replace("{version}", typeof(ClassProxySyntaxFactory) 55 | .Assembly 56 | .GetName() 57 | .Version 58 | .ToString()) 59 | ) 60 | ); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/SyntaxFactories/ModuleInitializerSyntaxFactory.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * ModuleInitializerSyntaxFactory.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | using System.IO; 8 | 9 | using Microsoft.CodeAnalysis; 10 | using NUnit.Framework; 11 | 12 | namespace Solti.Utils.Proxy.SyntaxFactories.Tests 13 | { 14 | using Internals; 15 | 16 | [TestFixture, Parallelizable(ParallelScope.All)] 17 | public class ModuleInitializerSyntaxFactoryTests 18 | { 19 | [Test] 20 | public void ResolveUnit_ShouldCreateTheDesiredUnit() 21 | { 22 | ModuleInitializerSyntaxFactory factory = new(SyntaxFactoryContext.Default with { OutputType = OutputType.Unit }); 23 | 24 | Assert.That 25 | ( 26 | factory.ResolveUnit(null, default).NormalizeWhitespace(eol: "\n").ToFullString(), 27 | Is.EqualTo 28 | ( 29 | File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ModuleInitializerAttribute.txt")).Replace("{version}", typeof(ModuleInitializerSyntaxFactory).Assembly.GetName().Version.ToString()) 30 | ) 31 | ); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/SyntaxFactories/SyntaxFactoryTestsBase.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * SyntaxFactoryTestsBase.cs * 3 | * * 4 | * Author: Denes Solti * 5 | ********************************************************************************/ 6 | using System; 7 | 8 | namespace Solti.Utils.Proxy.SyntaxFactories.Tests 9 | { 10 | using Internals; 11 | 12 | public class SyntaxFactoryTestsBase 13 | { 14 | public delegate void TestDelegate(object sender, T eventArg); 15 | 16 | internal interface IFoo // deliberately internal 17 | { 18 | int Foo(int a, out string b, ref TT c); 19 | void Bar(); 20 | T Prop { get; set; } 21 | T this[int i] { get; set; } 22 | event TestDelegate Event; 23 | } 24 | 25 | internal abstract class Foo 26 | { 27 | public virtual T Prop { get; protected set; } 28 | public abstract T this[int i] { get; protected set; } 29 | public abstract event TestDelegate Event; 30 | public virtual T Bar(ref T param1, TT param2) => param1; 31 | } 32 | 33 | protected interface IComplex 34 | { 35 | void Method(); 36 | int Property { get; } 37 | event Action Event; 38 | } 39 | 40 | internal static IEventInfo InterfaceEvent { get; } = MetadataEventInfo.CreateFrom(typeof(IFoo).GetEvent(nameof(IFoo.Event))); 41 | 42 | internal static IEventInfo ClassEvent { get; } = MetadataEventInfo.CreateFrom(typeof(Foo).GetEvent(nameof(Foo.Event))); 43 | 44 | internal static IPropertyInfo InterfaceProp { get; } = MetadataPropertyInfo.CreateFrom(typeof(IFoo).GetProperty(nameof(IFoo.Prop))); 45 | 46 | internal static IPropertyInfo ClassProp { get; } = MetadataPropertyInfo.CreateFrom(typeof(Foo).GetProperty(nameof(Foo.Prop))); 47 | 48 | internal static IPropertyInfo InterfaceIndexer { get; } = MetadataPropertyInfo.CreateFrom(typeof(IFoo).GetProperty("Item")); 49 | 50 | internal static IPropertyInfo ClassIndexer { get; } = MetadataPropertyInfo.CreateFrom(typeof(Foo).GetProperty("Item")); 51 | 52 | internal static IMethodInfo FooMethod { get; } = MetadataMethodInfo.CreateFrom(typeof(IFoo).GetMethod(nameof(IFoo.Foo))); 53 | 54 | internal static IMethodInfo BarMethod { get; } = MetadataMethodInfo.CreateFrom(typeof(IFoo).GetMethod(nameof(IFoo.Bar))); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /TEST/ProxyGen.Tests/runtimeconfig.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "configProperties": { 3 | "ProxyGen.LogDirectory": "..\\Logs" 4 | } 5 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # # 3 | # appveyor.yml # 4 | # # 5 | # Author: Denes Solti # 6 | # # 7 | ############################################################################# 8 | 9 | # common configuration for ALL branches 10 | image: Visual Studio 2022 11 | 12 | environment: 13 | COVERALLS_REPO_TOKEN: 14 | secure: Q9urfAztEC05u3MAy/b3GRvzVj15ny2RZJ2vRZnCBN+hXMWtyIBDfhKbbDDa3LRX 15 | GITHUB_REPO_TOKEN: 16 | secure: qJEufJv866eebR/Boe1UnV3p1arOIX9BTyqAQnAVY49h4Ks40Sr0rHb3iN70e7BZ 17 | NUGET_REPO_TOKEN: 18 | secure: 5qD6wDHtR+E5FGfo0z/HIf16LSagpoFmjcnFlfor6lzQ3kbF0t1bjv2RLFsdMD8O 19 | GITHUB_EMAIL: 20 | secure: Z6F+xZzdZzc5DMYB/4J0KUhyoTzTlXH75ciGl7hFH9s= 21 | # APPVEYOR_RDP_PASSWORD: 22 | # secure: IvxSZoWD+DpqNjUP7mFjPA== 23 | 24 | branches: 25 | # blacklist 26 | except: 27 | - gh-pages 28 | 29 | # prevent MSBuild from running 30 | build: off 31 | 32 | # enable RDP 33 | # init: 34 | # - ps: IEX ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 35 | 36 | # clone repo & submodules 37 | clone_script: 38 | - cmd: | 39 | git clone -q --recursive --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER% 40 | git checkout -qf %APPVEYOR_REPO_COMMIT% 41 | 42 | # load the build scripts 43 | before_test: 44 | - ps: | 45 | Set-Location -path ".\scripts" 46 | .(".\includes.ps1") 47 | Update-AppveyorBuild -Version "$($PROJECT.version).$($Env:APPVEYOR_BUILD_NUMBER)-$($Env:APPVEYOR_REPO_BRANCH)" 48 | Get-SysInfo 49 | 50 | test_script: 51 | - ps: Test 52 | 53 | after_test: 54 | - ps: Push-Test-Results 55 | 56 | deploy_script: 57 | - ps: Deploy 58 | 59 | # after build failure or success 60 | on_finish: 61 | - ps: | 62 | Push-Artifact "*.txt" 63 | Push-Artifact "BenchmarkDotNet.Artifacts\BenchmarkRun-*.log" 64 | # $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 65 | 66 | # override settings for `master` branch 67 | for: 68 | - 69 | branches: 70 | only: 71 | - master 72 | 73 | before_deploy: 74 | - ps: Git-Config 75 | 76 | deploy_script: 77 | # update documentation & publish nuget package 78 | - ps: | 79 | GH-Pages 80 | Deploy -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sholtee/proxygen/0ecd6213b3d73a567d5d65f6f7c632051ee8343c/icon.png -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "bin": "\\BIN", 3 | "variants": ["ROSLYN_LEGACY", "ROSLYN_LATEST"], 4 | "solution": "\\ProxyGen.sln", 5 | "app": "\\SRC\\ProxyGen.csproj", 6 | "vendor": "\\Vendor", 7 | "tests": "\\TEST\\ProxyGen.Tests\\ProxyGen.Tests.csproj", 8 | "coverage": ["Solti.Utils.Proxy.dll"], 9 | "artifacts": "\\Artifacts", 10 | "perftests": "\\PERF\\ProxyGen.Perf.csproj", 11 | "docsbranch": "gh-pages", 12 | "CI": "AppVeyor" 13 | } --------------------------------------------------------------------------------