From 931d57978be86a39169dae554cf99e484413a2ed Mon Sep 17 00:00:00 2001 From: "liuhaoyang.qz" Date: Sun, 12 Apr 2026 16:46:03 +0800 Subject: [PATCH 1/2] feat: refactor proxy emit to use AST architecture - Add ProxyBuilder/ directory with AST nodes, builders, and IL emit visitor - Nodes: ProxyTypeNode, MethodNode, ConstructorNode, PropertyNode, etc. - Builders: ClassProxyAstBuilder, InterfaceProxyAstBuilder, InterfaceImplAstBuilder - Visitors: ILEmitVisitor for IL generation from AST - Delete old Utils/ProxyGeneratorUtils.cs (1304 lines, replaced by AST) - Update ProxyTypeGenerator to use new ProxyTypeCompiler - Add tests: - AsyncExceptionInterfaceProxyTests - async exception proxy parity - ProxyMetadataParityTests - metadata equivalence validation - OpenGenericMethodTests - generic method tests enhanced - Add benchmarks: ProxyEmitBenchmarks for performance comparison - Update AspectActivator for compatibility --- .omc/graphs/emit-ast-production-hardening.md | 37 + .../Benchmarks/ProxyEmitBenchmarks.cs | 57 + .../AspectCore.Core.Benchmark/Program.cs | 5 +- .../DynamicProxy/AspectActivator.cs | 10 +- .../Builders/AttributeNodeFactory.cs | 25 + .../Builders/ClassProxyAstBuilder.cs | 257 ++++ .../Builders/GenericParameterNodeFactory.cs | 78 + .../ProxyBuilder/Builders/IProxyAstBuilder.cs | 9 + .../Builders/InterfaceImplAstBuilder.cs | 410 ++++++ .../Builders/InterfaceProxyAstBuilder.cs | 91 ++ .../Builders/MethodBodyFactory.cs | 79 + .../Builders/ParameterNodeFactory.cs | 95 ++ .../ProxyBuilder/ConstructorKind.cs | 10 + .../ProxyBuilder/Nodes/AttributeNode.cs | 26 + .../ProxyBuilder/Nodes/ConstructorNode.cs | 51 + .../ProxyBuilder/Nodes/FieldAssignmentNode.cs | 17 + .../ProxyBuilder/Nodes/FieldNode.cs | 23 + .../Nodes/GenericParameterNode.cs | 29 + .../ProxyBuilder/Nodes/MethodBodyNode.cs | 109 ++ .../ProxyBuilder/Nodes/MethodConstantNode.cs | 20 + .../ProxyBuilder/Nodes/MethodNode.cs | 51 + .../ProxyBuilder/Nodes/ParameterNode.cs | 37 + .../ProxyBuilder/Nodes/PropertyNode.cs | 43 + .../ProxyBuilder/Nodes/ProxyAstNode.cs | 7 + .../ProxyBuilder/Nodes/ProxyTypeNode.cs | 62 + .../ProxyBuilder/Nodes/TargetCreationNode.cs | 17 + .../DynamicProxy/ProxyBuilder/ProxyKind.cs | 9 + .../ProxyBuilder/ProxyTypeCompiler.cs | 197 +++ .../DynamicProxy/ProxyBuilder/ReturnKind.cs | 12 + .../ProxyBuilder/Visitors/ILEmitVisitor.cs | 718 +++++++++ .../Visitors/ILEmitVisitorContext.cs | 76 + .../ProxyBuilder/Visitors/IProxyAstVisitor.cs | 38 + .../DynamicProxy/ProxyTypeGenerator.cs | 6 +- .../Utils/ProxyGeneratorUtils.cs | 1304 ----------------- .../AsyncExceptionInterfaceProxyTests.cs | 93 ++ .../DynamicProxy/OpenGenericMethodTests.cs | 70 +- .../DynamicProxy/ProxyMetadataParityTests.cs | 149 ++ 37 files changed, 3003 insertions(+), 1324 deletions(-) create mode 100644 .omc/graphs/emit-ast-production-hardening.md create mode 100644 benchmark/AspectCore.Core.Benchmark/Benchmarks/ProxyEmitBenchmarks.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/AttributeNodeFactory.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ClassProxyAstBuilder.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/GenericParameterNodeFactory.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/IProxyAstBuilder.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/InterfaceImplAstBuilder.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/InterfaceProxyAstBuilder.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/MethodBodyFactory.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ParameterNodeFactory.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/ConstructorKind.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/AttributeNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ConstructorNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/FieldAssignmentNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/FieldNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/GenericParameterNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodBodyNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodConstantNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ParameterNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/PropertyNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ProxyAstNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ProxyTypeNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/TargetCreationNode.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/ProxyKind.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/ProxyTypeCompiler.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/ReturnKind.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitor.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitorContext.cs create mode 100644 src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/IProxyAstVisitor.cs delete mode 100644 src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs create mode 100644 tests/AspectCore.Tests/DynamicProxy/AsyncExceptionInterfaceProxyTests.cs create mode 100644 tests/AspectCore.Tests/DynamicProxy/ProxyMetadataParityTests.cs diff --git a/.omc/graphs/emit-ast-production-hardening.md b/.omc/graphs/emit-ast-production-hardening.md new file mode 100644 index 00000000..47b98c28 --- /dev/null +++ b/.omc/graphs/emit-ast-production-hardening.md @@ -0,0 +1,37 @@ +# Graph: emit-ast-production-hardening + +> Status: completed | Iteration: 3/6 + +## User Intent +- Original: 看一下当前做的 emit AST 的重构. 帮我继续优化到生产可用,以及完整的验证 +- Acceptance: 盘清当前 emit AST 重构状态与风险点;补齐达到生产可用所需的实现优化;完成独立、完整、可复述的验证,并报告剩余风险。 + +## Understanding +- 用户要在当前仓库中继续推进 emit AST 重构,不是从零开始。 +- 成功标准强调“生产可用”和“完整验证”,需要实现与独立验证分离。 +- 当前 emit AST 已接管 DynamicProxy 主生成路径,边界是“代理类型/成员/元数据 AST 化 + ILEmitVisitor 输出 IL”,不是方法体逐指令 AST 化。 +- 当前主要缺口不是主流程缺失,而是生产证据与硬化不足:interface proxy parity、open generic/generic method 行为覆盖、异步异常边角、proxy emit benchmark、netstandard/跨平台兼容性证据。 + +## Completed +### n1 @omc-explore — DONE_WITH_CONCERNS +- Summary: emit AST 重构主链路已落地,但还缺生产化验证闭环。 +- Findings: 主流程由 ProxyTypeCompiler + *AstBuilder + Nodes + ILEmitVisitor 组成;无旧新实现并存切换;测试偏 class-heavy,interface/generic/异常/性能/兼容性证据不足。 +- Deliverables: 核心代码地图、验证入口、最小后续执行清单。 + +### n2 @omc-deep-executor — DONE_WITH_CONCERNS +- Summary: 补齐 interface/generic/异常/性能/兼容性关键缺口,新增 benchmark,并做代表性本地验证。 +- Findings: 修复返回参数位置与 generic parameter attribute 回放;补 interface-only open generic stub 的 generic method 定义;收紧异步返回值 await 语义;新增 proxy emit benchmark。 +- Deliverables: 新增/修改 src、tests、benchmark 多处文件;本地验证通过(DynamicProxy 过滤测试、netstandard2.0/2.1 build、ProxyEmitBenchmarks 运行)。 + +### n3 @omc-verifier — DONE_WITH_CONCERNS +- Summary: 独立验证通过,本轮目标可认为完成,且显著接近生产可用。 +- Findings: DynamicProxy 子集、核心测试工程全框架、整仓 solution build/test、netstandard build、ProxyEmitBenchmarks 均通过;未发现新的功能性回归。 +- Deliverables: 独立验收结论为 PASS;明确两个残留边界——`ThrowInUncontinuedTasks` 未重验、`AspectCore.Core.csproj` 存在 `.net9.0` 单框架构建瑕疵。 + +## In Flight + +## Pending + + +## Budget +- Iterations: 3 / 6 diff --git a/benchmark/AspectCore.Core.Benchmark/Benchmarks/ProxyEmitBenchmarks.cs b/benchmark/AspectCore.Core.Benchmark/Benchmarks/ProxyEmitBenchmarks.cs new file mode 100644 index 00000000..cd7cdfb7 --- /dev/null +++ b/benchmark/AspectCore.Core.Benchmark/Benchmarks/ProxyEmitBenchmarks.cs @@ -0,0 +1,57 @@ +using System; +using AspectCore.Configuration; +using AspectCore.DynamicProxy; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; + +namespace AspectCore.Core.Benchmark.Benchmarks +{ + [AllStatisticsColumn] + [MemoryDiagnoser] + [StopOnFirstError] + [ShortRunJob] + public class ProxyEmitBenchmarks + { + [Benchmark] + public Type CreateInterfaceProxyType_WithoutImplementation() + { + return CreateTypeGenerator().CreateInterfaceProxyType(typeof(IProxyEmitBenchmarkService)); + } + + [Benchmark] + public Type CreateInterfaceProxyType_WithImplementation() + { + return CreateTypeGenerator().CreateInterfaceProxyType(typeof(IProxyEmitBenchmarkService), typeof(ProxyEmitBenchmarkService)); + } + + [Benchmark(Baseline = true)] + public Type CreateClassProxyType() + { + return CreateTypeGenerator().CreateClassProxyType(typeof(ProxyEmitBenchmarkService), typeof(ProxyEmitBenchmarkService)); + } + + private static ProxyTypeGenerator CreateTypeGenerator() + { + var configuration = new AspectConfiguration(); + configuration.Interceptors.AddDelegate((ctx, next) => next(ctx)); + return new ProxyTypeGenerator(new AspectValidatorBuilder(configuration)); + } + + public interface IProxyEmitBenchmarkService + { + string Name { get; set; } + + string Echo(string value); + } + + public class ProxyEmitBenchmarkService : IProxyEmitBenchmarkService + { + public virtual string Name { get; set; } + + public virtual string Echo(string value) + { + return value; + } + } + } +} diff --git a/benchmark/AspectCore.Core.Benchmark/Program.cs b/benchmark/AspectCore.Core.Benchmark/Program.cs index 4544ee87..a5fc4bf3 100644 --- a/benchmark/AspectCore.Core.Benchmark/Program.cs +++ b/benchmark/AspectCore.Core.Benchmark/Program.cs @@ -1,4 +1,3 @@ -using AspectCore.Core.Benchmark.Benchmarks; using BenchmarkDotNet.Running; namespace AspectCore.Core.Benchmark @@ -7,7 +6,7 @@ public static class Program { public static void Main(string[] args) { - BenchmarkRunner.Run(); + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); } } -} \ No newline at end of file +} diff --git a/src/AspectCore.Core/DynamicProxy/AspectActivator.cs b/src/AspectCore.Core/DynamicProxy/AspectActivator.cs index 05c5f474..c350beea 100644 --- a/src/AspectCore.Core/DynamicProxy/AspectActivator.cs +++ b/src/AspectCore.Core/DynamicProxy/AspectActivator.cs @@ -76,8 +76,9 @@ public async Task InvokeTask(AspectActivatorContext activatorC case null: return default; case Task taskWithResult: - return taskWithResult.Result; - case Task _: + return await taskWithResult; + case Task task: + await task; return default; default: throw new AspectInvalidCastException(context, $"Unable to cast object of type '{context.ReturnValue.GetType()}' to type '{typeof(Task)}'."); @@ -119,8 +120,9 @@ public async ValueTask InvokeValueTask(AspectActivatorContext case null: return default; case ValueTask taskWithResult: - return taskWithResult.Result; + return await taskWithResult; case ValueTask task: + await task; return default; default: throw new AspectInvalidCastException(context, $"Unable to cast object of type '{context.ReturnValue.GetType()}' to type '{typeof(ValueTask)}'."); @@ -139,4 +141,4 @@ public async ValueTask InvokeValueTask(AspectActivatorContext } } } -} \ No newline at end of file +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/AttributeNodeFactory.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/AttributeNodeFactory.cs new file mode 100644 index 00000000..cb781aff --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/AttributeNodeFactory.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Builders +{ + internal static class AttributeNodeFactory + { + public static AttributeNode CreateMarker(Type attributeType) + { + return new AttributeNode(attributeType); + } + + public static List FromCustomAttributes(IEnumerable attributeDataList) + { + var result = new List(); + foreach (var data in attributeDataList) + { + result.Add(new AttributeNode(data)); + } + return result; + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ClassProxyAstBuilder.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ClassProxyAstBuilder.cs new file mode 100644 index 00000000..a4101c31 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ClassProxyAstBuilder.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AspectCore.DynamicProxy; +using AspectCore.Utils; +using AspectCore.Extensions.Reflection; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Builders +{ + internal class ClassProxyBuilder : IProxyTypeBuilder + { + private static readonly HashSet Ignores = new HashSet { "Finalize" }; + + private readonly string _name; + private readonly Type _serviceType; + private readonly Type _implType; + private readonly Type[] _additionalInterfaces; + private readonly IAspectValidator _aspectValidator; + + public ClassProxyBuilder( + string name, + Type serviceType, + Type implType, + Type[] additionalInterfaces, + IAspectValidator aspectValidator) + { + _name = name; + _serviceType = serviceType; + _implType = implType; + _additionalInterfaces = additionalInterfaces; + _aspectValidator = aspectValidator; + } + + public ProxyTypeNode[] Build() + { + var interfaces = _additionalInterfaces.Distinct().ToArray(); + var genericParams = GenericParameterNodeFactory.FromType(_serviceType); + + var attributes = new List + { + AttributeNodeFactory.CreateMarker(typeof(NonAspectAttribute)), + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + + // Inherit impl type's attributes + attributes.AddRange(AttributeNodeFactory.FromCustomAttributes(_implType.CustomAttributes)); + + var fields = new List + { + new FieldNode("_activatorFactory", typeof(IAspectActivatorFactory), FieldAttributes.Private), + new FieldNode("_implementation", _serviceType, FieldAttributes.Private) + }; + + var constructors = BuildConstructors(); + var methods = new List(); + var properties = new List(); + var methodConstants = new List(); + + BuildClassMethods(methods, methodConstants); + BuildClassProperties(properties, methods, methodConstants); + BuildAdditionalInterfaceMembers(methods, properties, methodConstants); + + return new[] + { + new ProxyTypeNode( + _name, + ProxyKind.ClassProxy, + _serviceType, + _implType, + interfaces, + genericParams, + attributes, + fields, + constructors, + methods, + properties, + methodConstants) + }; + } + + private List BuildConstructors() + { + var constructors = _implType.GetTypeInfo().DeclaredConstructors + .Where(c => !c.IsStatic && (c.IsPublic || c.IsFamily || c.IsFamilyAndAssembly || c.IsFamilyOrAssembly)) + .ToArray(); + + if (constructors.Length == 0) + { + throw new InvalidOperationException( + $"A suitable constructor for type {_serviceType.FullName} could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor."); + } + + var result = new List(constructors.Length); + foreach (var ctor in constructors) + { + var parameterTypes = ctor.GetParameters().Select(p => p.ParameterType).ToArray(); + var allParamTypes = new[] { typeof(IAspectActivatorFactory) }.Concat(parameterTypes).ToArray(); + + var ctorAttributes = new List + { + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + ctorAttributes.AddRange(AttributeNodeFactory.FromCustomAttributes(ctor.CustomAttributes)); + + var parameters = ParameterNodeFactory.FromConstructor(ctor); + + result.Add(new ConstructorNode( + ConstructorKind.ClassProxyCtorFromBase, + ctor.Attributes, + ctor.CallingConvention, + allParamTypes, + ctor, + parameters, + ctorAttributes, + new[] + { + new FieldAssignmentNode("_activatorFactory", 1) + }, + targetCreation: null)); + } + return result; + } + + private void BuildClassMethods(List methods, List methodConstants) + { + foreach (var method in _serviceType.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(x => !x.IsPropertyBinding())) + { + if (!method.IsVisibleAndVirtual() || Ignores.Contains(method.Name)) + continue; + + var implMethod = InterfaceImplBuilder.ResolveImplementationMethod(method, _implType); + var body = MethodBodyFactory.DecideBody(method, implMethod, _aspectValidator, _serviceType); + + var attributes = MethodBuilderConstants.OverrideMethodAttributes; + if (method.Attributes.HasFlag(MethodAttributes.Public)) + attributes |= MethodAttributes.Public; + if (method.Attributes.HasFlag(MethodAttributes.Family)) + attributes |= MethodAttributes.Family; + if (method.Attributes.HasFlag(MethodAttributes.FamORAssem)) + attributes |= MethodAttributes.FamORAssem; + + var node = InterfaceImplBuilder.BuildProxyMethod( + method, implMethod, method.Name, attributes, body, null, methodConstants); + methods.Add(node); + } + } + + private void BuildClassProperties(List properties, List methods, List methodConstants) + { + foreach (var property in _serviceType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + { + if (!property.IsVisibleAndVirtual()) + continue; + + MethodNode getMethod = null; + MethodNode setMethod = null; + + if (property.CanRead) + { + var method = property.GetMethod; + var implMethod = InterfaceImplBuilder.ResolveImplementationMethod(method, _implType); + var body = MethodBodyFactory.DecideBody(method, implMethod, _aspectValidator, _serviceType); + + var attributes = MethodBuilderConstants.OverrideMethodAttributes; + if (method.Attributes.HasFlag(MethodAttributes.Public)) + attributes |= MethodAttributes.Public; + if (method.Attributes.HasFlag(MethodAttributes.Family)) + attributes |= MethodAttributes.Family; + if (method.Attributes.HasFlag(MethodAttributes.FamORAssem)) + attributes |= MethodAttributes.FamORAssem; + + getMethod = InterfaceImplBuilder.BuildProxyMethod( + method, implMethod, method.Name, attributes, body, null, methodConstants); + } + + if (property.CanWrite) + { + var method = property.SetMethod; + var implMethod = InterfaceImplBuilder.ResolveImplementationMethod(method, _implType); + var body = MethodBodyFactory.DecideBody(method, implMethod, _aspectValidator, _serviceType); + + var attributes = MethodBuilderConstants.OverrideMethodAttributes; + if (method.Attributes.HasFlag(MethodAttributes.Public)) + attributes |= MethodAttributes.Public; + if (method.Attributes.HasFlag(MethodAttributes.Family)) + attributes |= MethodAttributes.Family; + if (method.Attributes.HasFlag(MethodAttributes.FamORAssem)) + attributes |= MethodAttributes.FamORAssem; + + setMethod = InterfaceImplBuilder.BuildProxyMethod( + method, implMethod, method.Name, attributes, body, null, methodConstants); + } + + var attrs = new List + { + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + attrs.AddRange(AttributeNodeFactory.FromCustomAttributes(property.CustomAttributes)); + + properties.Add(new PropertyNode(property.Name, property.PropertyType, property.Attributes, attrs, getMethod, setMethod)); + } + } + + private void BuildAdditionalInterfaceMembers(List methods, List properties, List methodConstants) + { + foreach (var iface in _additionalInterfaces) + { + foreach (var method in iface.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) + { + var implMethod = InterfaceImplBuilder.ResolveImplementationMethod(method, _implType); + var body = MethodBodyFactory.DecideBody(method, implMethod, _aspectValidator, _serviceType); + var node = InterfaceImplBuilder.BuildProxyMethod( + method, implMethod, method.GetName(), + MethodBuilderConstants.ExplicitMethodAttributes, body, method, methodConstants); + methods.Add(node); + } + + foreach (var property in iface.GetTypeInfo().DeclaredProperties) + { + MethodNode getMethod = null; + MethodNode setMethod = null; + + if (property.CanRead) + { + var method = property.GetMethod; + var implMethod = InterfaceImplBuilder.ResolveImplementationMethod(method, _implType); + var body = MethodBodyFactory.DecideBody(method, implMethod, _aspectValidator, _serviceType); + getMethod = InterfaceImplBuilder.BuildProxyMethod( + method, implMethod, method.GetName(), + MethodBuilderConstants.ExplicitMethodAttributes, body, method, methodConstants); + } + + if (property.CanWrite) + { + var method = property.SetMethod; + var implMethod = InterfaceImplBuilder.ResolveImplementationMethod(method, _implType); + var body = MethodBodyFactory.DecideBody(method, implMethod, _aspectValidator, _serviceType); + setMethod = InterfaceImplBuilder.BuildProxyMethod( + method, implMethod, method.GetName(), + MethodBuilderConstants.ExplicitMethodAttributes, body, method, methodConstants); + } + + var attrs = new List + { + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + attrs.AddRange(AttributeNodeFactory.FromCustomAttributes(property.CustomAttributes)); + + properties.Add(new PropertyNode(property.GetDisplayName(), property.PropertyType, property.Attributes, attrs, getMethod, setMethod)); + } + } + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/GenericParameterNodeFactory.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/GenericParameterNodeFactory.cs new file mode 100644 index 00000000..d447ebca --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/GenericParameterNodeFactory.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Builders +{ + internal static class GenericParameterNodeFactory + { + public static List FromType(Type targetType) + { + if (!targetType.GetTypeInfo().IsGenericTypeDefinition) + return new List(); + + return BuildFromGenericArguments(targetType.GetTypeInfo().GetGenericArguments(), isTypeLevel: true); + } + + public static List FromMethod(MethodInfo method) + { + if (!method.IsGenericMethod) + return new List(); + + return BuildFromGenericArguments(method.GetGenericArguments(), isTypeLevel: false); + } + + private static List BuildFromGenericArguments(Type[] genericArguments, bool isTypeLevel) + { + var result = new List(genericArguments.Length); + foreach (var arg in genericArguments) + { + var argInfo = arg.GetTypeInfo(); + var constraints = isTypeLevel + ? ToClassGenericParameterAttributes(argInfo.GenericParameterAttributes) + : argInfo.GenericParameterAttributes; + + Type baseTypeConstraint = null; + var interfaceConstraints = new List(); + + foreach (var constraint in argInfo.GetGenericParameterConstraints()) + { + var constraintInfo = constraint.GetTypeInfo(); + if (constraintInfo.IsClass) + baseTypeConstraint = constraintInfo.AsType(); + if (constraintInfo.IsInterface) + interfaceConstraints.Add(constraintInfo.AsType()); + } + + var attributes = AttributeNodeFactory.FromCustomAttributes(arg.CustomAttributes).ToArray(); + + result.Add(new GenericParameterNode( + argInfo.Name, + constraints, + baseTypeConstraint, + interfaceConstraints.ToArray(), + attributes)); + } + return result; + } + + private static GenericParameterAttributes ToClassGenericParameterAttributes(GenericParameterAttributes attributes) + { + if (attributes == GenericParameterAttributes.None) + return GenericParameterAttributes.None; + if (attributes.HasFlag(GenericParameterAttributes.SpecialConstraintMask)) + return GenericParameterAttributes.SpecialConstraintMask; + if (attributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)) + return GenericParameterAttributes.NotNullableValueTypeConstraint; + if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint) && attributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)) + return GenericParameterAttributes.ReferenceTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint; + if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)) + return GenericParameterAttributes.ReferenceTypeConstraint; + if (attributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)) + return GenericParameterAttributes.DefaultConstructorConstraint; + return GenericParameterAttributes.None; + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/IProxyAstBuilder.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/IProxyAstBuilder.cs new file mode 100644 index 00000000..2a52d305 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/IProxyAstBuilder.cs @@ -0,0 +1,9 @@ +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Builders +{ + internal interface IProxyTypeBuilder + { + ProxyTypeNode[] Build(); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/InterfaceImplAstBuilder.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/InterfaceImplAstBuilder.cs new file mode 100644 index 00000000..cbc824ba --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/InterfaceImplAstBuilder.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AspectCore.DynamicProxy; +using AspectCore.Utils; +using AspectCore.Extensions.Reflection; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Builders +{ + internal class InterfaceImplBuilder : IProxyTypeBuilder + { + private readonly string _implName; + private readonly string _proxyName; + private readonly Type _interfaceType; + private readonly Type[] _additionalInterfaces; + private readonly IAspectValidator _aspectValidator; + + public InterfaceImplBuilder( + string implName, + string proxyName, + Type interfaceType, + Type[] additionalInterfaces, + IAspectValidator aspectValidator) + { + _implName = implName; + _proxyName = proxyName; + _interfaceType = interfaceType; + _additionalInterfaces = additionalInterfaces; + _aspectValidator = aspectValidator; + } + + public ProxyTypeNode[] Build() + { + var interfaceTypes = new[] { _interfaceType }.Concat(_additionalInterfaces).Distinct().ToArray(); + var stubNode = BuildStubType(interfaceTypes); + var proxyNode = BuildProxyType(interfaceTypes); + return new[] { stubNode, proxyNode }; + } + + public ProxyTypeNode BuildStubOnly() + { + var interfaceTypes = new[] { _interfaceType }.Concat(_additionalInterfaces).Distinct().ToArray(); + return BuildStubType(interfaceTypes); + } + + public ProxyTypeNode BuildProxyOnly(string proxyName, Type stubImplType) + { + var interfaceTypes = new[] { _interfaceType }.Concat(_additionalInterfaces).Distinct().ToArray(); + return BuildProxyTypeWithStub(proxyName, stubImplType, interfaceTypes); + } + + private ProxyTypeNode BuildStubType(Type[] interfaceTypes) + { + var genericParams = GenericParameterNodeFactory.FromType(_interfaceType); + + var constructor = new ConstructorNode( + ConstructorKind.DefaultObjectCtor, + MethodAttributes.Public, + MethodUtils.ObjectCtor.CallingConvention, + Type.EmptyTypes, + baseConstructor: null, + parameters: null, + attributes: null, + fieldAssignments: null, + targetCreation: null); + + var methods = new List(); + var properties = new List(); + + foreach (var iface in interfaceTypes) + { + foreach (var method in iface.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) + { + if (!method.IsAbstract) continue; + methods.Add(BuildStubMethod(method)); + } + + foreach (var property in iface.GetTypeInfo().DeclaredProperties) + { + properties.Add(BuildStubProperty(property)); + } + } + + return new ProxyTypeNode( + _implName, + ProxyKind.InterfaceImpl, + _interfaceType, + typeof(object), + interfaceTypes, + genericParams, + attributes: null, + fields: null, + new[] { constructor }, + methods, + properties, + methodConstants: null); + } + + private MethodNode BuildStubMethod(MethodInfo method) + { + var genericParameters = GenericParameterNodeFactory.FromMethod(method); + var parameters = ParameterNodeFactory.FromMethod(method); + var attributes = AttributeNodeFactory.FromCustomAttributes(method.CustomAttributes); + + return new MethodNode( + method, + implementationMethod: null, + method.Name, + MethodBuilderConstants.InterfaceMethodAttributes, + new StubBody(method.ReturnType), + parameters, + genericParameters, + attributes, + overridesMethod: method); + } + + private PropertyNode BuildStubProperty(PropertyInfo property) + { + var backingField = new FieldNode( + $"<{property.Name}>k__BackingField", + property.PropertyType, + FieldAttributes.Private); + + MethodNode getMethod = null; + MethodNode setMethod = null; + + if (property.CanRead) + { + getMethod = new MethodNode( + property.GetMethod, + implementationMethod: null, + property.GetMethod.Name, + MethodBuilderConstants.InterfaceMethodAttributes, + new BackingFieldGetBody(backingField.Name), + parameters: ParameterNodeFactory.FromMethod(property.GetMethod), + genericParameters: GenericParameterNodeFactory.FromMethod(property.GetMethod), + attributes: AttributeNodeFactory.FromCustomAttributes(property.GetMethod.CustomAttributes), + overridesMethod: property.GetMethod); + } + + if (property.CanWrite) + { + setMethod = new MethodNode( + property.SetMethod, + implementationMethod: null, + property.SetMethod.Name, + MethodBuilderConstants.InterfaceMethodAttributes, + new BackingFieldSetBody(backingField.Name), + parameters: ParameterNodeFactory.FromMethod(property.SetMethod), + genericParameters: GenericParameterNodeFactory.FromMethod(property.SetMethod), + attributes: AttributeNodeFactory.FromCustomAttributes(property.SetMethod.CustomAttributes), + overridesMethod: property.SetMethod); + } + + var attrs = AttributeNodeFactory.FromCustomAttributes(property.CustomAttributes); + + return new PropertyNode( + property.Name, + property.PropertyType, + property.Attributes, + attrs, + getMethod, + setMethod, + backingField); + } + + private ProxyTypeNode BuildProxyType(Type[] interfaceTypes) + { + return BuildProxyTypeCore(_proxyName, null, interfaceTypes); + } + + private ProxyTypeNode BuildProxyTypeWithStub(string proxyName, Type stubImplType, Type[] interfaceTypes) + { + return BuildProxyTypeCore(proxyName, stubImplType, interfaceTypes); + } + + private ProxyTypeNode BuildProxyTypeCore(string proxyName, Type stubImplType, Type[] interfaceTypes) + { + var genericParams = GenericParameterNodeFactory.FromType(_interfaceType); + + var attributes = new List + { + AttributeNodeFactory.CreateMarker(typeof(NonAspectAttribute)), + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + + var fields = new List + { + new FieldNode("_activatorFactory", typeof(IAspectActivatorFactory), FieldAttributes.Private), + new FieldNode("_implementation", _interfaceType, FieldAttributes.Private) + }; + + var constructor = new ConstructorNode( + ConstructorKind.InterfaceProxyCtorWithFactory, + MethodAttributes.Public, + MethodUtils.ObjectCtor.CallingConvention, + new[] { typeof(IAspectActivatorFactory) }, + baseConstructor: null, + parameters: null, + attributes: null, + new[] + { + new FieldAssignmentNode("_activatorFactory", 1) + }, + stubImplType != null ? new TargetCreationNode(stubImplType, "_implementation") : null); + + var methods = new List(); + var properties = new List(); + var methodConstants = new List(); + + BuildInterfaceProxyMembers( + _interfaceType, _additionalInterfaces, stubImplType, _aspectValidator, + methods, properties, methodConstants); + + return new ProxyTypeNode( + proxyName, + ProxyKind.InterfaceProxy, + _interfaceType, + typeof(object), + interfaceTypes, + genericParams, + attributes, + fields, + new[] { constructor }, + methods, + properties, + methodConstants); + } + + internal static void BuildInterfaceProxyMembers( + Type interfaceType, + Type[] additionalInterfaces, + Type implType, + IAspectValidator aspectValidator, + List methods, + List properties, + List methodConstants) + { + var resolvedImplType = implType ?? interfaceType; + + // Primary interface methods + foreach (var method in interfaceType.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) + { + var implMethod = ResolveImplementationMethod(method, resolvedImplType); + var body = MethodBodyFactory.DecideBody(method, implMethod, aspectValidator, interfaceType); + var node = BuildProxyMethod(method, implMethod, method.Name, + MethodBuilderConstants.InterfaceMethodAttributes, body, method, methodConstants); + methods.Add(node); + } + + // Additional interface methods (explicit) + foreach (var iface in additionalInterfaces) + { + foreach (var method in iface.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) + { + var implMethod = ResolveImplementationMethod(method, resolvedImplType); + var body = MethodBodyFactory.DecideBody(method, implMethod, aspectValidator, interfaceType); + var node = BuildProxyMethod(method, implMethod, method.GetName(), + MethodBuilderConstants.ExplicitMethodAttributes, body, method, methodConstants); + methods.Add(node); + } + } + + // Primary interface properties + foreach (var property in interfaceType.GetTypeInfo().DeclaredProperties) + { + properties.Add(BuildProxyProperty(property, property.Name, resolvedImplType, aspectValidator, + interfaceType, MethodBuilderConstants.InterfaceMethodAttributes, methods, methodConstants)); + } + + // Additional interface properties (explicit) + foreach (var iface in additionalInterfaces) + { + foreach (var property in iface.GetTypeInfo().DeclaredProperties) + { + properties.Add(BuildProxyProperty(property, property.GetDisplayName(), resolvedImplType, aspectValidator, + interfaceType, MethodBuilderConstants.ExplicitMethodAttributes, methods, methodConstants)); + } + } + } + + private static PropertyNode BuildProxyProperty( + PropertyInfo property, + string name, + Type implType, + IAspectValidator aspectValidator, + Type serviceType, + MethodAttributes methodAttrs, + List methods, + List methodConstants) + { + MethodNode getMethod = null; + MethodNode setMethod = null; + + if (property.CanRead) + { + var method = property.GetMethod; + var implMethod = ResolveImplementationMethod(method, implType); + var body = MethodBodyFactory.DecideBody(method, implMethod, aspectValidator, serviceType); + var overrides = methodAttrs == MethodBuilderConstants.ExplicitMethodAttributes ? method : method; + getMethod = BuildProxyMethod(method, implMethod, methodAttrs == MethodBuilderConstants.ExplicitMethodAttributes ? method.GetName() : method.Name, + methodAttrs, body, method, methodConstants); + } + + if (property.CanWrite) + { + var method = property.SetMethod; + var implMethod = ResolveImplementationMethod(method, implType); + var body = MethodBodyFactory.DecideBody(method, implMethod, aspectValidator, serviceType); + setMethod = BuildProxyMethod(method, implMethod, methodAttrs == MethodBuilderConstants.ExplicitMethodAttributes ? method.GetName() : method.Name, + methodAttrs, body, method, methodConstants); + } + + var attrs = new List + { + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + attrs.AddRange(AttributeNodeFactory.FromCustomAttributes(property.CustomAttributes)); + + return new PropertyNode(name, property.PropertyType, property.Attributes, attrs, getMethod, setMethod); + } + + internal static MethodNode BuildProxyMethod( + MethodInfo serviceMethod, + MethodInfo implMethod, + string name, + MethodAttributes attributes, + MethodBodyNode body, + MethodInfo overridesMethod, + List methodConstants) + { + var genericParams = GenericParameterNodeFactory.FromMethod(serviceMethod); + var parameters = ParameterNodeFactory.FromMethod(serviceMethod); + + var attrs = new List + { + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + attrs.AddRange(AttributeNodeFactory.FromCustomAttributes(serviceMethod.CustomAttributes)); + + // Register method constants for aspect activator body + if (body is AspectActivatorBody activatorBody && !activatorBody.IsGeneric) + { + var serviceKey = $"service{serviceMethod.GetDisplayName()}"; + var implKey = $"impl{implMethod.GetDisplayName()}"; + var proxyKey = $"proxy{serviceMethod.GetDisplayName()}"; + + methodConstants.Add(new MethodConstantNode(serviceKey, serviceMethod)); + methodConstants.Add(new MethodConstantNode(implKey, implMethod)); + // proxyMethod will be the generated MethodBuilder - stored as null, resolved during visit + methodConstants.Add(new MethodConstantNode(proxyKey, null)); + } + + return new MethodNode( + serviceMethod, + implMethod, + name, + attributes, + body, + parameters, + genericParams, + attrs, + overridesMethod); + } + + internal static MethodInfo ResolveImplementationMethod(MethodInfo method, Type implType) + { + if (method.DeclaringType == implType) + { + return method; + } + + if (method.DeclaringType != null + && method.DeclaringType.GetTypeInfo().IsGenericType + && implType.GetTypeInfo().IsGenericTypeDefinition + && method.DeclaringType.GetGenericTypeDefinition() == implType) + { + return method; + } + + var implementationMethod = implType.GetTypeInfo().GetMethodBySignature(method); + if (implementationMethod != null) + return implementationMethod; + + var interfaces = implType.GetInterfaces(); + if (interfaces == null || interfaces.Length == 0) + throw new MissingMethodException($"Type '{implType}' does not contain a method '{method}'."); + + var abstractInterfaces = interfaces.Where(f => f.GetCustomAttribute(typeof(AbstractInterceptorAttribute)) != null).ToArray(); + var searchInterfaces = abstractInterfaces.Length > 0 ? abstractInterfaces : interfaces; + + foreach (var iface in searchInterfaces) + { + implementationMethod = iface.GetTypeInfo().GetMethodBySignature(method); + if (implementationMethod != null) return implementationMethod; + } + + throw new MissingMethodException($"Type '{implType}' does not contain a method '{method}'."); + } + } + + internal static class MethodBuilderConstants + { + internal const MethodAttributes ExplicitMethodAttributes = MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; + internal const MethodAttributes InterfaceMethodAttributes = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; + internal const MethodAttributes OverrideMethodAttributes = MethodAttributes.HideBySig | MethodAttributes.Virtual; + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/InterfaceProxyAstBuilder.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/InterfaceProxyAstBuilder.cs new file mode 100644 index 00000000..a8b01212 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/InterfaceProxyAstBuilder.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AspectCore.DynamicProxy; +using AspectCore.Utils; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Builders +{ + internal class InterfaceProxyBuilder : IProxyTypeBuilder + { + private readonly string _name; + private readonly Type _interfaceType; + private readonly Type _implType; + private readonly Type[] _additionalInterfaces; + private readonly IAspectValidator _aspectValidator; + + public InterfaceProxyBuilder( + string name, + Type interfaceType, + Type implType, + Type[] additionalInterfaces, + IAspectValidator aspectValidator) + { + _name = name; + _interfaceType = interfaceType; + _implType = implType; + _additionalInterfaces = additionalInterfaces; + _aspectValidator = aspectValidator; + } + + public ProxyTypeNode[] Build() + { + var interfaces = new[] { _interfaceType }.Concat(_additionalInterfaces).Distinct().ToArray(); + var genericParams = GenericParameterNodeFactory.FromType(_interfaceType); + + var attributes = new List + { + AttributeNodeFactory.CreateMarker(typeof(NonAspectAttribute)), + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + + var fields = new List + { + new FieldNode("_activatorFactory", typeof(IAspectActivatorFactory), FieldAttributes.Private), + new FieldNode("_implementation", _interfaceType, FieldAttributes.Private) + }; + + var constructor = new ConstructorNode( + ConstructorKind.InterfaceProxyCtorWithFactoryAndTarget, + MethodAttributes.Public, + MethodUtils.ObjectCtor.CallingConvention, + new[] { typeof(IAspectActivatorFactory), _interfaceType }, + baseConstructor: null, + parameters: null, + attributes: null, + new[] + { + new FieldAssignmentNode("_activatorFactory", 1), + new FieldAssignmentNode("_implementation", 2) + }, + targetCreation: null); + + var methods = new List(); + var properties = new List(); + var methodConstants = new List(); + + InterfaceImplBuilder.BuildInterfaceProxyMembers( + _interfaceType, _additionalInterfaces, _implType, _aspectValidator, + methods, properties, methodConstants); + + return new[] + { + new ProxyTypeNode( + _name, + ProxyKind.InterfaceProxy, + _interfaceType, + typeof(object), + interfaces, + genericParams, + attributes, + fields, + new[] { constructor }, + methods, + properties, + methodConstants) + }; + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/MethodBodyFactory.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/MethodBodyFactory.cs new file mode 100644 index 00000000..5e3cad95 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/MethodBodyFactory.cs @@ -0,0 +1,79 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using AspectCore.DynamicProxy; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Builders +{ + internal static class MethodBodyFactory + { + private const string TargetFieldName = "_implementation"; + + public static MethodBodyNode DecideBody( + MethodInfo serviceMethod, + MethodInfo implementationMethod, + IAspectValidator validator, + Type serviceType) + { + if (serviceMethod.IsNonAspect()) + return BuildDelegationBody(serviceMethod, implementationMethod, serviceType); + + if (validator.Validate(serviceMethod, true) || validator.Validate(implementationMethod, false)) + return BuildAspectActivatorBody(serviceMethod, implementationMethod); + + return BuildDelegationBody(serviceMethod, implementationMethod, serviceType); + } + + public static MethodBodyNode BuildDelegationBody( + MethodInfo serviceMethod, + MethodInfo implementationMethod, + Type serviceType) + { + if (serviceType.GetTypeInfo().IsInterface || !implementationMethod.IsExplicit()) + { + return new DirectDelegationBody( + TargetFieldName, + implementationMethod.IsExplicit() ? serviceMethod : implementationMethod, + serviceMethod, + implementationMethod.IsCallvirt(), + serviceType); + } + + return new ReflectorDelegationBody(implementationMethod, serviceMethod); + } + + public static AspectActivatorBody BuildAspectActivatorBody( + MethodInfo serviceMethod, + MethodInfo implementationMethod) + { + return new AspectActivatorBody( + serviceMethod, + implementationMethod, + serviceMethod.IsGenericMethodDefinition, + DetermineReturnKind(serviceMethod), + serviceMethod.ReturnType); + } + + public static StubBody BuildStubBody(MethodInfo method) + { + return new StubBody(method.ReturnType); + } + + public static ReturnKind DetermineReturnKind(MethodInfo method) + { + if (method.ReturnType == typeof(void)) + return ReturnKind.Void; + if (method.ReturnType == typeof(Task)) + return ReturnKind.Task; + if (method.IsReturnTask()) + return ReturnKind.TaskOfT; + if (method.ReturnType == typeof(ValueTask)) + return ReturnKind.ValueTask; + if (method.IsReturnValueTask()) + return ReturnKind.ValueTaskOfT; + return ReturnKind.Sync; + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ParameterNodeFactory.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ParameterNodeFactory.cs new file mode 100644 index 00000000..c3ba26e3 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Builders/ParameterNodeFactory.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using AspectCore.DynamicProxy; +using AspectCore.Extensions.Reflection; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Builders +{ + internal static class ParameterNodeFactory + { + public static List FromMethod(MethodInfo method) + { + var result = new List(); + foreach (var param in method.GetParameters()) + { + result.Add(FromParameterInfo(param)); + } + // Return parameter + result.Add(FromReturnParameter(method.ReturnParameter)); + return result; + } + + public static List FromConstructor(ConstructorInfo constructor) + { + var result = new List(); + foreach (var param in constructor.GetParameters()) + { + result.Add(FromParameterInfo(param)); + } + return result; + } + + private static ParameterNode FromParameterInfo(ParameterInfo param) + { + bool hasDefault = param.HasDefaultValueByAttributes(); + object defaultValue = null; + + if (hasDefault) + { + defaultValue = TryGetDefaultValue(param); + } + + var attributes = new List + { + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + attributes.AddRange(AttributeNodeFactory.FromCustomAttributes(param.CustomAttributes)); + + return new ParameterNode( + param.Position, + param.Name, + param.ParameterType, + param.Attributes, + hasDefault, + defaultValue, + attributes); + } + + private static ParameterNode FromReturnParameter(ParameterInfo param) + { + var attributes = new List + { + AttributeNodeFactory.CreateMarker(typeof(DynamicallyAttribute)) + }; + attributes.AddRange(AttributeNodeFactory.FromCustomAttributes(param.CustomAttributes)); + + return new ParameterNode( + -1, + param.Name, + param.ParameterType, + param.Attributes, + false, + null, + attributes); + } + + private static object TryGetDefaultValue(ParameterInfo param) + { + try + { + return param.DefaultValue; + } + catch (FormatException) when (param.ParameterType == typeof(DateTime)) + { + return null; + } + catch (FormatException) when (param.ParameterType.IsEnum) + { + return null; + } + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ConstructorKind.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ConstructorKind.cs new file mode 100644 index 00000000..794fc76b --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ConstructorKind.cs @@ -0,0 +1,10 @@ +namespace AspectCore.DynamicProxy.ProxyBuilder +{ + internal enum ConstructorKind + { + DefaultObjectCtor, + InterfaceProxyCtorWithFactory, + InterfaceProxyCtorWithFactoryAndTarget, + ClassProxyCtorFromBase + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/AttributeNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/AttributeNode.cs new file mode 100644 index 00000000..5fdc8fbc --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/AttributeNode.cs @@ -0,0 +1,26 @@ +using System; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class AttributeNode : ProxyBuilderNode + { + public CustomAttributeData CustomAttributeData { get; } + + public Type MarkerAttributeType { get; } + + public bool IsMarker => MarkerAttributeType != null; + + public AttributeNode(CustomAttributeData data) + { + CustomAttributeData = data ?? throw new ArgumentNullException(nameof(data)); + } + + public AttributeNode(Type markerType) + { + MarkerAttributeType = markerType ?? throw new ArgumentNullException(nameof(markerType)); + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitAttribute(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ConstructorNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ConstructorNode.cs new file mode 100644 index 00000000..624b0560 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ConstructorNode.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class ConstructorNode : ProxyBuilderNode + { + public ConstructorKind Kind { get; } + + public ConstructorInfo BaseConstructor { get; } + + public MethodAttributes MethodAttributes { get; } + + public CallingConventions CallingConvention { get; } + + public IReadOnlyList Parameters { get; } + + public IReadOnlyList Attributes { get; } + + public IReadOnlyList FieldAssignments { get; } + + public TargetCreationNode TargetCreation { get; } + + public Type[] ParameterTypes { get; } + + public ConstructorNode( + ConstructorKind kind, + MethodAttributes methodAttributes, + CallingConventions callingConvention, + Type[] parameterTypes, + ConstructorInfo baseConstructor, + IReadOnlyList parameters, + IReadOnlyList attributes, + IReadOnlyList fieldAssignments, + TargetCreationNode targetCreation) + { + Kind = kind; + MethodAttributes = methodAttributes; + CallingConvention = callingConvention; + ParameterTypes = parameterTypes ?? Type.EmptyTypes; + BaseConstructor = baseConstructor; + Parameters = parameters ?? Array.Empty(); + Attributes = attributes ?? Array.Empty(); + FieldAssignments = fieldAssignments ?? Array.Empty(); + TargetCreation = targetCreation; + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitConstructor(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/FieldAssignmentNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/FieldAssignmentNode.cs new file mode 100644 index 00000000..d1c313ea --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/FieldAssignmentNode.cs @@ -0,0 +1,17 @@ +using System; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class FieldAssignmentNode + { + public string FieldName { get; } + + public int SourceArgIndex { get; } + + public FieldAssignmentNode(string fieldName, int sourceArgIndex) + { + FieldName = fieldName ?? throw new ArgumentNullException(nameof(fieldName)); + SourceArgIndex = sourceArgIndex; + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/FieldNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/FieldNode.cs new file mode 100644 index 00000000..0e03efac --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/FieldNode.cs @@ -0,0 +1,23 @@ +using System; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class FieldNode : ProxyBuilderNode + { + public string Name { get; } + + public Type FieldType { get; } + + public FieldAttributes Accessibility { get; } + + public FieldNode(string name, Type fieldType, FieldAttributes accessibility = FieldAttributes.Private) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + FieldType = fieldType ?? throw new ArgumentNullException(nameof(fieldType)); + Accessibility = accessibility; + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitField(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/GenericParameterNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/GenericParameterNode.cs new file mode 100644 index 00000000..b1ecf5ed --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/GenericParameterNode.cs @@ -0,0 +1,29 @@ +using System; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class GenericParameterNode : ProxyBuilderNode + { + public string Name { get; } + + public GenericParameterAttributes Constraints { get; } + + public Type BaseTypeConstraint { get; } + + public Type[] InterfaceConstraints { get; } + + public AttributeNode[] Attributes { get; } + + public GenericParameterNode(string name, GenericParameterAttributes constraints, Type baseTypeConstraint, Type[] interfaceConstraints, AttributeNode[] attributes) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + Constraints = constraints; + BaseTypeConstraint = baseTypeConstraint; + InterfaceConstraints = interfaceConstraints ?? Array.Empty(); + Attributes = attributes ?? Array.Empty(); + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitGenericParameter(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodBodyNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodBodyNode.cs new file mode 100644 index 00000000..387b469c --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodBodyNode.cs @@ -0,0 +1,109 @@ +using System; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal abstract class MethodBodyNode + { + public abstract void Accept(IProxyBuilderVisitor visitor); + } + + internal class DirectDelegationBody : MethodBodyNode + { + public string TargetFieldName { get; } + + public MethodInfo TargetMethod { get; } + + public MethodInfo ServiceMethod { get; } + + public bool IsCallvirt { get; } + + public Type ServiceType { get; } + + public DirectDelegationBody(string targetFieldName, MethodInfo targetMethod, MethodInfo serviceMethod, bool isCallvirt, Type serviceType) + { + TargetFieldName = targetFieldName ?? throw new ArgumentNullException(nameof(targetFieldName)); + TargetMethod = targetMethod ?? throw new ArgumentNullException(nameof(targetMethod)); + ServiceMethod = serviceMethod ?? throw new ArgumentNullException(nameof(serviceMethod)); + IsCallvirt = isCallvirt; + ServiceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType)); + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitDirectDelegationBody(this); + } + + internal class ReflectorDelegationBody : MethodBodyNode + { + public MethodInfo ImplementationMethod { get; } + + public MethodInfo ServiceMethod { get; } + + public ReflectorDelegationBody(MethodInfo implementationMethod, MethodInfo serviceMethod) + { + ImplementationMethod = implementationMethod ?? throw new ArgumentNullException(nameof(implementationMethod)); + ServiceMethod = serviceMethod ?? throw new ArgumentNullException(nameof(serviceMethod)); + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitReflectorDelegationBody(this); + } + + internal class AspectActivatorBody : MethodBodyNode + { + public MethodInfo ServiceMethod { get; } + + public MethodInfo ImplementationMethod { get; } + + public bool IsGeneric { get; } + + public ReturnKind ReturnKind { get; } + + public Type ReturnType { get; } + + public AspectActivatorBody(MethodInfo serviceMethod, MethodInfo implementationMethod, bool isGeneric, ReturnKind returnKind, Type returnType) + { + ServiceMethod = serviceMethod ?? throw new ArgumentNullException(nameof(serviceMethod)); + ImplementationMethod = implementationMethod ?? throw new ArgumentNullException(nameof(implementationMethod)); + IsGeneric = isGeneric; + ReturnKind = returnKind; + ReturnType = returnType; + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitAspectActivatorBody(this); + } + + internal class StubBody : MethodBodyNode + { + public Type ReturnType { get; } + + public StubBody(Type returnType) + { + ReturnType = returnType; + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitStubBody(this); + } + + internal class BackingFieldGetBody : MethodBodyNode + { + public string FieldName { get; } + + public BackingFieldGetBody(string fieldName) + { + FieldName = fieldName ?? throw new ArgumentNullException(nameof(fieldName)); + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitBackingFieldGetBody(this); + } + + internal class BackingFieldSetBody : MethodBodyNode + { + public string FieldName { get; } + + public BackingFieldSetBody(string fieldName) + { + FieldName = fieldName ?? throw new ArgumentNullException(nameof(fieldName)); + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitBackingFieldSetBody(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodConstantNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodConstantNode.cs new file mode 100644 index 00000000..3b064c8a --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodConstantNode.cs @@ -0,0 +1,20 @@ +using System; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class MethodConstantNode : ProxyBuilderNode + { + public string Key { get; } + + public MethodInfo Method { get; } + + public MethodConstantNode(string key, MethodInfo method) + { + Key = key ?? throw new ArgumentNullException(nameof(key)); + Method = method; + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitMethodConstant(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodNode.cs new file mode 100644 index 00000000..24132cd0 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/MethodNode.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class MethodNode : ProxyBuilderNode + { + public MethodInfo ServiceMethod { get; } + + public MethodInfo ImplementationMethod { get; } + + public string Name { get; } + + public MethodAttributes MethodAttributes { get; } + + public MethodBodyNode Body { get; } + + public IReadOnlyList Parameters { get; } + + public IReadOnlyList GenericParameters { get; } + + public IReadOnlyList Attributes { get; } + + public MethodInfo OverridesMethod { get; } + + public MethodNode( + MethodInfo serviceMethod, + MethodInfo implementationMethod, + string name, + MethodAttributes methodAttributes, + MethodBodyNode body, + IReadOnlyList parameters, + IReadOnlyList genericParameters, + IReadOnlyList attributes, + MethodInfo overridesMethod) + { + ServiceMethod = serviceMethod ?? throw new ArgumentNullException(nameof(serviceMethod)); + ImplementationMethod = implementationMethod; + Name = name ?? throw new ArgumentNullException(nameof(name)); + MethodAttributes = methodAttributes; + Body = body ?? throw new ArgumentNullException(nameof(body)); + Parameters = parameters ?? Array.Empty(); + GenericParameters = genericParameters ?? Array.Empty(); + Attributes = attributes ?? Array.Empty(); + OverridesMethod = overridesMethod; + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitMethod(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ParameterNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ParameterNode.cs new file mode 100644 index 00000000..3d8e2133 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ParameterNode.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class ParameterNode : ProxyBuilderNode + { + public int Position { get; } + + public string Name { get; } + + public Type ParameterType { get; } + + public ParameterAttributes Attributes { get; } + + public object DefaultValue { get; } + + public bool HasDefaultValue { get; } + + public IReadOnlyList CustomAttributes { get; } + + public ParameterNode(int position, string name, Type parameterType, ParameterAttributes attributes, + bool hasDefaultValue, object defaultValue, IReadOnlyList customAttributes) + { + Position = position; + Name = name; + ParameterType = parameterType ?? throw new ArgumentNullException(nameof(parameterType)); + Attributes = attributes; + HasDefaultValue = hasDefaultValue; + DefaultValue = defaultValue; + CustomAttributes = customAttributes ?? Array.Empty(); + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitParameter(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/PropertyNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/PropertyNode.cs new file mode 100644 index 00000000..47d0682e --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/PropertyNode.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class PropertyNode : ProxyBuilderNode + { + public string Name { get; } + + public Type PropertyType { get; } + + public PropertyAttributes PropertyAttributes { get; } + + public IReadOnlyList Attributes { get; } + + public MethodNode GetMethod { get; } + + public MethodNode SetMethod { get; } + + public FieldNode BackingField { get; } + + public PropertyNode( + string name, + Type propertyType, + PropertyAttributes propertyAttributes, + IReadOnlyList attributes, + MethodNode getMethod, + MethodNode setMethod, + FieldNode backingField = null) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + PropertyType = propertyType ?? throw new ArgumentNullException(nameof(propertyType)); + PropertyAttributes = propertyAttributes; + Attributes = attributes ?? Array.Empty(); + GetMethod = getMethod; + SetMethod = setMethod; + BackingField = backingField; + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitProperty(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ProxyAstNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ProxyAstNode.cs new file mode 100644 index 00000000..3e811672 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ProxyAstNode.cs @@ -0,0 +1,7 @@ +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal abstract class ProxyBuilderNode + { + public abstract void Accept(IProxyBuilderVisitor visitor); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ProxyTypeNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ProxyTypeNode.cs new file mode 100644 index 00000000..803ac704 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/ProxyTypeNode.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class ProxyTypeNode : ProxyBuilderNode + { + public string Name { get; } + + public ProxyKind ProxyKind { get; } + + public Type ServiceType { get; } + + public Type ParentType { get; } + + public Type[] Interfaces { get; } + + public IReadOnlyList GenericParameters { get; } + + public IReadOnlyList Attributes { get; } + + public IReadOnlyList Fields { get; } + + public IReadOnlyList Constructors { get; } + + public IReadOnlyList Methods { get; } + + public IReadOnlyList Properties { get; } + + public IReadOnlyList MethodConstants { get; } + + public ProxyTypeNode( + string name, + ProxyKind proxyKind, + Type serviceType, + Type parentType, + Type[] interfaces, + IReadOnlyList genericParameters, + IReadOnlyList attributes, + IReadOnlyList fields, + IReadOnlyList constructors, + IReadOnlyList methods, + IReadOnlyList properties, + IReadOnlyList methodConstants) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + ProxyKind = proxyKind; + ServiceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType)); + ParentType = parentType ?? throw new ArgumentNullException(nameof(parentType)); + Interfaces = interfaces ?? Type.EmptyTypes; + GenericParameters = genericParameters ?? Array.Empty(); + Attributes = attributes ?? Array.Empty(); + Fields = fields ?? Array.Empty(); + Constructors = constructors ?? Array.Empty(); + Methods = methods ?? Array.Empty(); + Properties = properties ?? Array.Empty(); + MethodConstants = methodConstants ?? Array.Empty(); + } + + public override void Accept(IProxyBuilderVisitor visitor) => visitor.VisitProxyType(this); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/TargetCreationNode.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/TargetCreationNode.cs new file mode 100644 index 00000000..bad99fac --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Nodes/TargetCreationNode.cs @@ -0,0 +1,17 @@ +using System; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Nodes +{ + internal class TargetCreationNode + { + public Type ImplType { get; } + + public string TargetFieldName { get; } + + public TargetCreationNode(Type implType, string targetFieldName) + { + ImplType = implType ?? throw new ArgumentNullException(nameof(implType)); + TargetFieldName = targetFieldName ?? throw new ArgumentNullException(nameof(targetFieldName)); + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ProxyKind.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ProxyKind.cs new file mode 100644 index 00000000..b42f8caa --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ProxyKind.cs @@ -0,0 +1,9 @@ +namespace AspectCore.DynamicProxy.ProxyBuilder +{ + internal enum ProxyKind + { + InterfaceImpl, + InterfaceProxy, + ClassProxy + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ProxyTypeCompiler.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ProxyTypeCompiler.cs new file mode 100644 index 00000000..4163b0ea --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ProxyTypeCompiler.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; +using AspectCore.DynamicProxy.ProxyBuilder.Builders; +using AspectCore.DynamicProxy.ProxyBuilder.Visitors; +using AspectCore.Extensions.Reflection; + +namespace AspectCore.DynamicProxy.ProxyBuilder +{ + internal class ProxyTypeCompiler + { + private const string ProxyNameSpace = "AspectCore.DynamicGenerated"; + private const string ProxyAssemblyName = "AspectCore.DynamicProxy.Generator"; + private readonly ModuleBuilder _moduleBuilder; + private readonly Dictionary _definedTypes; + private readonly object _lock = new object(); + private readonly ProxyNameUtils _proxyNameUtils; + + public ProxyTypeCompiler() + { + var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(ProxyAssemblyName), AssemblyBuilderAccess.RunAndCollect); + _moduleBuilder = asmBuilder.DefineDynamicModule("core"); + _definedTypes = new Dictionary(); + _proxyNameUtils = new ProxyNameUtils(); + } + + internal Type CreateInterfaceProxy(Type interfaceType, Type[] additionalInterfaces, IAspectValidator aspectValidator) + { + if (!interfaceType.GetTypeInfo().IsVisible() || !interfaceType.GetTypeInfo().IsInterface) + { + throw new InvalidOperationException($"Validate '{interfaceType}' failed because the type does not satisfy the visible conditions."); + } + + lock (_lock) + { + var name = _proxyNameUtils.GetInterfaceImplTypeFullName(interfaceType); + if (!_definedTypes.TryGetValue(name, out Type type)) + { + type = CreateInterfaceImplInternal(name, interfaceType, additionalInterfaces, aspectValidator); + _definedTypes[name] = type; + } + return type; + } + } + + internal Type CreateInterfaceProxy(Type interfaceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator) + { + if (!interfaceType.GetTypeInfo().IsVisible() || !interfaceType.GetTypeInfo().IsInterface) + { + throw new InvalidOperationException($"Validate '{interfaceType}' failed because the type does not satisfy the visible conditions."); + } + + lock (_lock) + { + var name = _proxyNameUtils.GetProxyTypeName(interfaceType, implType); + if (!_definedTypes.TryGetValue(name, out Type type)) + { + type = CreateInterfaceProxyInternal(name, interfaceType, implType, additionalInterfaces, aspectValidator); + _definedTypes[name] = type; + } + return type; + } + } + + internal Type CreateClassProxy(Type serviceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator) + { + if (!serviceType.GetTypeInfo().IsVisible() || !serviceType.GetTypeInfo().IsClass) + { + throw new InvalidOperationException($"Validate '{serviceType}' failed because the type does not satisfy the visible conditions."); + } + if (!implType.GetTypeInfo().CanInherited()) + { + throw new InvalidOperationException($"Validate '{implType}' failed because the type does not satisfy the condition to be inherited."); + } + + lock (_lock) + { + var name = _proxyNameUtils.GetProxyTypeName(serviceType, implType); + if (!_definedTypes.TryGetValue(name, out Type type)) + { + type = CreateClassProxyInternal(name, serviceType, implType, additionalInterfaces, aspectValidator); + _definedTypes[name] = type; + } + return type; + } + } + + private Type CreateInterfaceImplInternal(string name, Type interfaceType, Type[] additionalInterfaces, IAspectValidator aspectValidator) + { + // Phase 1: Build and compile the stub implementation type + var builder = new InterfaceImplBuilder(name, null, interfaceType, additionalInterfaces, aspectValidator); + var stubNode = builder.BuildStubOnly(); + + var ctx = new ILEmitVisitorContext(_moduleBuilder); + var visitor = new ILEmitVisitor(ctx); + var stubType = visitor.VisitProxyType(stubNode); + + // Phase 2: Now compute proxy name using the compiled stub type, then build and compile proxy + var proxyName = _proxyNameUtils.GetProxyTypeName( + _proxyNameUtils.GetInterfaceImplTypeName(interfaceType), interfaceType, stubType); + + var proxyNode = builder.BuildProxyOnly(proxyName, stubType); + + // Use a fresh visitor for the proxy type (same module) + var proxyVisitor = new ILEmitVisitor(new ILEmitVisitorContext(_moduleBuilder)); + return proxyVisitor.VisitProxyType(proxyNode); + } + + private Type CreateInterfaceProxyInternal(string name, Type interfaceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator) + { + var builder = new InterfaceProxyBuilder(name, interfaceType, implType, additionalInterfaces, aspectValidator); + var nodes = builder.Build(); + + var visitor = new ILEmitVisitor(new ILEmitVisitorContext(_moduleBuilder)); + var types = visitor.VisitAll(nodes); + + return types[0]; + } + + private Type CreateClassProxyInternal(string name, Type serviceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator) + { + var builder = new ClassProxyBuilder(name, serviceType, implType, additionalInterfaces, aspectValidator); + var nodes = builder.Build(); + + var visitor = new ILEmitVisitor(new ILEmitVisitorContext(_moduleBuilder)); + var types = visitor.VisitAll(nodes); + + return types[0]; + } + + private class ProxyNameUtils + { + private readonly Dictionary _indexs = new Dictionary(); + private readonly Dictionary, string> _indexMaps = new Dictionary, string>(); + + private string GetProxyTypeIndex(string className, Type serviceType, Type implementationType) + { + ProxyNameIndex nameIndex; + if (!_indexs.TryGetValue(className, out nameIndex)) + { + nameIndex = new ProxyNameIndex(); + _indexs[className] = nameIndex; + } + var key = Tuple.Create(serviceType, implementationType); + string index; + if (!_indexMaps.TryGetValue(key, out index)) + { + var tempIndex = nameIndex.GenIndex(); + index = tempIndex == 0 ? string.Empty : tempIndex.ToString(); + _indexMaps[key] = index; + } + Debug.WriteLine($"{className}-{serviceType}-{implementationType}-{index}"); + return index; + } + + public string GetInterfaceImplTypeName(Type interfaceType) + { + var className = interfaceType.GetReflector().DisplayName; + if (className.StartsWith("I", StringComparison.Ordinal)) + { + className = className.Substring(1); + } + return className; + } + + public string GetInterfaceImplTypeFullName(Type interfaceType) + { + var className = GetInterfaceImplTypeName(interfaceType); + return $"{ProxyNameSpace}.{className}{GetProxyTypeIndex(className, interfaceType, interfaceType)}"; + } + + public string GetProxyTypeName(Type serviceType, Type implType) + { + return $"{ProxyNameSpace}.{implType.GetReflector().DisplayName}{GetProxyTypeIndex(implType.GetReflector().DisplayName, serviceType, implType)}"; + } + + public string GetProxyTypeName(string className, Type serviceType, Type implType) + { + return $"{ProxyNameSpace}.{className}{GetProxyTypeIndex(className, serviceType, implType)}"; + } + } + + private class ProxyNameIndex + { + private int _index = -1; + + public int GenIndex() + { + return Interlocked.Increment(ref _index); + } + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ReturnKind.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ReturnKind.cs new file mode 100644 index 00000000..47358522 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/ReturnKind.cs @@ -0,0 +1,12 @@ +namespace AspectCore.DynamicProxy.ProxyBuilder +{ + internal enum ReturnKind + { + Void, + Sync, + Task, + TaskOfT, + ValueTask, + ValueTaskOfT + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitor.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitor.cs new file mode 100644 index 00000000..526efe22 --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitor.cs @@ -0,0 +1,718 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading.Tasks; +using AspectCore.DynamicProxy; +using AspectCore.Extensions.Reflection; +using AspectCore.Extensions.Reflection.Emit; +using AspectCore.Utils; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Visitors +{ + internal class ILEmitVisitor : IProxyBuilderVisitor + { + private readonly ILEmitVisitorContext _ctx; + + public ILEmitVisitor(ILEmitVisitorContext context) + { + _ctx = context ?? throw new ArgumentNullException(nameof(context)); + } + + public Type[] VisitAll(ProxyTypeNode[] nodes) + { + var results = new Type[nodes.Length]; + for (int i = 0; i < nodes.Length; i++) + results[i] = VisitProxyType(nodes[i]); + return results; + } + + public Type VisitProxyType(ProxyTypeNode node) + { + var typeBuilder = _ctx.ModuleBuilder.DefineType( + node.Name, + node.ProxyKind == ProxyKind.InterfaceImpl + ? TypeAttributes.Class | TypeAttributes.Public + : TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, + node.ParentType, + node.Interfaces); + + _ctx.TypeBuilder = typeBuilder; + _ctx.ServiceType = node.ServiceType; + _ctx.Fields.Clear(); + + // Generic parameters + if (node.GenericParameters.Count > 0) + DefineGenericParameters(typeBuilder, node.GenericParameters); + + // Attributes + foreach (var attr in node.Attributes) + VisitAttribute(attr); + + // Fields + foreach (var field in node.Fields) + VisitField(field); + + // Method constants + if (node.ProxyKind != ProxyKind.InterfaceImpl) + { + _ctx.MethodConstants = new MethodConstantTable(typeBuilder); + foreach (var mc in node.MethodConstants) + VisitMethodConstant(mc); + } + + // Constructors + foreach (var ctor in node.Constructors) + VisitConstructor(ctor); + + // Methods + foreach (var method in node.Methods) + VisitMethod(method); + + // Properties + foreach (var prop in node.Properties) + VisitProperty(prop); + + // Compile + if (_ctx.MethodConstants != null) + _ctx.MethodConstants.Compile(); + + _ctx.MethodConstants = null; + return typeBuilder.CreateTypeInfo().AsType(); + } + + public void VisitField(FieldNode node) + { + var field = _ctx.TypeBuilder.DefineField(node.Name, node.FieldType, node.Accessibility); + _ctx.Fields[node.Name] = field; + } + + public void VisitConstructor(ConstructorNode node) + { + switch (node.Kind) + { + case ConstructorKind.DefaultObjectCtor: + EmitDefaultObjectCtor(node); + break; + case ConstructorKind.InterfaceProxyCtorWithFactory: + EmitInterfaceProxyCtorWithFactory(node); + break; + case ConstructorKind.InterfaceProxyCtorWithFactoryAndTarget: + EmitInterfaceProxyCtorWithFactoryAndTarget(node); + break; + case ConstructorKind.ClassProxyCtorFromBase: + EmitClassProxyCtor(node); + break; + } + } + + private void EmitDefaultObjectCtor(ConstructorNode node) + { + var ctorBuilder = _ctx.TypeBuilder.DefineConstructor( + MethodAttributes.Public, MethodUtils.ObjectCtor.CallingConvention, Type.EmptyTypes); + var il = ctorBuilder.GetILGenerator(); + il.EmitThis(); + il.Emit(OpCodes.Call, MethodUtils.ObjectCtor); + il.Emit(OpCodes.Ret); + } + + private void EmitInterfaceProxyCtorWithFactory(ConstructorNode node) + { + var ctorBuilder = _ctx.TypeBuilder.DefineConstructor( + MethodAttributes.Public, MethodUtils.ObjectCtor.CallingConvention, + new[] { typeof(IAspectActivatorFactory) }); + + ctorBuilder.DefineParameter(1, ParameterAttributes.None, "_activatorFactory"); + + var il = ctorBuilder.GetILGenerator(); + il.EmitThis(); + il.Emit(OpCodes.Call, MethodUtils.ObjectCtor); + + // Field assignments + foreach (var fa in node.FieldAssignments) + { + il.EmitThis(); + il.EmitLoadArg(fa.SourceArgIndex); + il.Emit(OpCodes.Stfld, _ctx.Fields[fa.FieldName]); + } + + // Create target instance: this._implementation = new StubImplType() + if (node.TargetCreation != null) + { + il.EmitThis(); + il.Emit(OpCodes.Newobj, node.TargetCreation.ImplType.GetTypeInfo().GetConstructor(Type.EmptyTypes)); + il.Emit(OpCodes.Stfld, _ctx.Fields[node.TargetCreation.TargetFieldName]); + } + + il.Emit(OpCodes.Ret); + } + + private void EmitInterfaceProxyCtorWithFactoryAndTarget(ConstructorNode node) + { + var ctorBuilder = _ctx.TypeBuilder.DefineConstructor( + MethodAttributes.Public, MethodUtils.ObjectCtor.CallingConvention, node.ParameterTypes); + + ctorBuilder.DefineParameter(1, ParameterAttributes.None, "_activatorFactory"); + ctorBuilder.DefineParameter(2, ParameterAttributes.None, "_implementation"); + + var il = ctorBuilder.GetILGenerator(); + il.EmitThis(); + il.Emit(OpCodes.Call, MethodUtils.ObjectCtor); + + foreach (var fa in node.FieldAssignments) + { + il.EmitThis(); + il.EmitLoadArg(fa.SourceArgIndex); + il.Emit(OpCodes.Stfld, _ctx.Fields[fa.FieldName]); + } + + il.Emit(OpCodes.Ret); + } + + private void EmitClassProxyCtor(ConstructorNode node) + { + var ctorBuilder = _ctx.TypeBuilder.DefineConstructor( + node.MethodAttributes, node.CallingConvention, node.ParameterTypes); + + // Attributes + foreach (var attr in node.Attributes) + SetCustomAttribute(ctorBuilder, attr); + + // Parameters: first param is IAspectActivatorFactory (offset 1), then base ctor params (offset 2+) + ctorBuilder.DefineParameter(1, ParameterAttributes.None, "aspectContextFactory"); + if (node.Parameters.Count > 0) + { + var paramOffset = 2; + for (var i = 0; i < node.Parameters.Count; i++) + { + var p = node.Parameters[i]; + var pb = ctorBuilder.DefineParameter(i + paramOffset, p.Attributes, p.Name); + if (p.HasDefaultValue) + { + try { pb.SetConstant(p.DefaultValue); } catch { } + } + foreach (var attr in p.CustomAttributes) + SetCustomAttribute(pb, attr); + } + } + + var il = ctorBuilder.GetILGenerator(); + + // this._activatorFactory = arg1 + il.EmitThis(); + il.EmitLoadArg(1); + il.Emit(OpCodes.Stfld, _ctx.Fields["_activatorFactory"]); + + // this._implementation = this (class proxy wraps itself) + il.EmitThis(); + il.EmitThis(); + il.Emit(OpCodes.Stfld, _ctx.Fields["_implementation"]); + + // base(arg2, arg3, ...) + il.EmitThis(); + var totalParams = node.ParameterTypes.Length; + for (var i = 2; i <= totalParams; i++) + il.EmitLoadArg(i); + il.Emit(OpCodes.Call, node.BaseConstructor); + + il.Emit(OpCodes.Ret); + } + + public void VisitMethod(MethodNode node) + { + var methodBuilder = _ctx.TypeBuilder.DefineMethod( + node.Name, node.MethodAttributes, + node.ServiceMethod.CallingConvention, + node.ServiceMethod.ReturnType, + node.ServiceMethod.GetParameterTypes()); + + _ctx.CurrentMethodBuilder = methodBuilder; + + // Generic parameters + if (node.GenericParameters.Count > 0) + DefineMethodGenericParameters(methodBuilder, node.GenericParameters); + + // Attributes + foreach (var attr in node.Attributes) + SetCustomAttribute(methodBuilder, attr); + + // Parameters + EmitMethodParameters(node, methodBuilder); + + // Override + if (node.OverridesMethod != null) + _ctx.TypeBuilder.DefineMethodOverride(methodBuilder, node.OverridesMethod); + + // Body + _ctx.CurrentILGenerator = methodBuilder.GetILGenerator(); + node.Body.Accept(this); + } + + private void EmitMethodParameters(MethodNode node, MethodBuilder methodBuilder) + { + if (node.Parameters.Count == 0) return; + + // Regular parameters (position > 0) come first, return parameter (position 0) last + foreach (var param in node.Parameters) + { + if (param.Position < 0) + { + // Return parameter + var pb = methodBuilder.DefineParameter(0, param.Attributes, param.Name); + foreach (var attr in param.CustomAttributes) + SetCustomAttribute(pb, attr); + } + else + { + var pb = methodBuilder.DefineParameter(param.Position + 1, param.Attributes, param.Name); + if (param.HasDefaultValue) + { + try + { + CopyDefaultValueConstant(param, pb); + } + catch { } + } + foreach (var attr in param.CustomAttributes) + SetCustomAttribute(pb, attr); + } + } + } + + public void VisitProperty(PropertyNode node) + { + // Backing field for stub properties + if (node.BackingField != null && !_ctx.Fields.ContainsKey(node.BackingField.Name)) + { + var field = _ctx.TypeBuilder.DefineField(node.BackingField.Name, node.BackingField.FieldType, node.BackingField.Accessibility); + _ctx.Fields[node.BackingField.Name] = field; + } + + var propertyBuilder = _ctx.TypeBuilder.DefineProperty( + node.Name, node.PropertyAttributes, node.PropertyType, Type.EmptyTypes); + + foreach (var attr in node.Attributes) + SetCustomAttribute(propertyBuilder, attr); + + if (node.GetMethod != null) + { + VisitMethod(node.GetMethod); + propertyBuilder.SetGetMethod(_ctx.CurrentMethodBuilder); + } + + if (node.SetMethod != null) + { + VisitMethod(node.SetMethod); + propertyBuilder.SetSetMethod(_ctx.CurrentMethodBuilder); + } + } + + public void VisitParameter(ParameterNode node) + { + // Handled inline in EmitMethodParameters / EmitClassProxyCtor + } + + public void VisitGenericParameter(GenericParameterNode node) + { + // Handled inline in DefineGenericParameters + } + + public void VisitAttribute(AttributeNode node) + { + var builder = node.IsMarker + ? BuildCustomAttribute(node.MarkerAttributeType) + : BuildCustomAttribute(node.CustomAttributeData); + _ctx.TypeBuilder.SetCustomAttribute(builder); + } + + public void VisitMethodConstant(MethodConstantNode node) + { + // For proxy methods, the MethodBuilder is not yet created; store null and update during body emit + _ctx.MethodConstants.AddMethod(node.Key, node.Method); + } + + // --- Method body visitors --- + + public void VisitDirectDelegationBody(DirectDelegationBody node) + { + var il = _ctx.CurrentILGenerator; + var parameters = node.ServiceMethod.GetParameterTypes(); + + il.EmitThis(); + il.Emit(OpCodes.Ldfld, _ctx.Fields[node.TargetFieldName]); + for (int i = 1; i <= parameters.Length; i++) + il.EmitLoadArg(i); + + var callOpCode = node.IsCallvirt ? OpCodes.Callvirt : OpCodes.Call; + il.Emit(callOpCode, node.TargetMethod); + il.Emit(OpCodes.Ret); + } + + public void VisitReflectorDelegationBody(ReflectorDelegationBody node) + { + var il = _ctx.CurrentILGenerator; + var parameters = node.ServiceMethod.GetParameterTypes(); + + var reflectorLocal = il.DeclareLocal(typeof(MethodReflector)); + var argsLocal = il.DeclareLocal(typeof(object[])); + var returnLocal = il.DeclareLocal(typeof(object)); + + il.EmitMethod(node.ImplementationMethod); + il.Emit(OpCodes.Call, MethodUtils.GetMethodReflector); + il.Emit(OpCodes.Stloc, reflectorLocal); + + il.EmitInt(parameters.Length); + il.Emit(OpCodes.Newarr, typeof(object)); + for (var i = 0; i < parameters.Length; i++) + { + il.Emit(OpCodes.Dup); + il.EmitInt(i); + il.EmitLoadArg(i + 1); + if (parameters[i].IsByRef) + { + il.EmitLdRef(parameters[i]); + il.EmitConvertToObject(parameters[i].GetElementType()); + } + else + { + il.EmitConvertToObject(parameters[i]); + } + il.Emit(OpCodes.Stelem_Ref); + } + il.Emit(OpCodes.Stloc, argsLocal); + + il.Emit(OpCodes.Ldloc, reflectorLocal); + il.EmitThis(); + il.Emit(OpCodes.Ldloc, argsLocal); + il.Emit(OpCodes.Callvirt, MethodUtils.ReflectorInvoke); + il.Emit(OpCodes.Stloc, returnLocal); + + // Write back byref parameters + for (var i = 0; i < parameters.Length; i++) + { + if (parameters[i].IsByRef) + { + var byrefToType = parameters[i].GetElementType(); + il.EmitLoadArg(i + 1); + il.Emit(OpCodes.Ldloc, argsLocal); + il.EmitInt(i); + il.Emit(OpCodes.Ldelem_Ref); + il.EmitConvertFromObject(byrefToType); + il.EmitStRef(byrefToType); + } + } + + if (!node.ServiceMethod.IsVoid()) + { + il.Emit(OpCodes.Ldloc, returnLocal); + il.EmitConvertFromObject(node.ServiceMethod.ReturnType); + } + + il.Emit(OpCodes.Ret); + } + + public void VisitAspectActivatorBody(AspectActivatorBody node) + { + var il = _ctx.CurrentILGenerator; + var methodBuilder = _ctx.CurrentMethodBuilder; + var activatorContext = il.DeclareLocal(typeof(AspectActivatorContext)); + LocalBuilder returnValue = null; + + // Emit metadata initialization + EmitInitializeMetaData(il, node, methodBuilder); + + il.Emit(OpCodes.Newobj, MethodUtils.AspectActivatorContextCtor); + il.Emit(OpCodes.Stloc, activatorContext); + + il.EmitThis(); + il.Emit(OpCodes.Ldfld, _ctx.Fields["_activatorFactory"]); + il.Emit(OpCodes.Callvirt, MethodUtils.CreateAspectActivator); + il.Emit(OpCodes.Ldloc, activatorContext); + + EmitReturnValue(il, node); + + if (node.ReturnKind != ReturnKind.Void) + { + returnValue = il.DeclareLocal(node.ServiceMethod.ReturnType); + il.Emit(OpCodes.Stloc, returnValue); + } + + // Write back byref parameters + var parameterTypes = node.ServiceMethod.GetParameterTypes(); + if (parameterTypes.Any(x => x.IsByRef)) + { + var parameters = il.DeclareLocal(typeof(object[])); + il.Emit(OpCodes.Ldloca, activatorContext); + il.Emit(OpCodes.Call, MethodUtils.GetParameters); + il.Emit(OpCodes.Stloc, parameters); + for (var i = 0; i < parameterTypes.Length; i++) + { + if (parameterTypes[i].IsByRef) + { + var byrefToType = parameterTypes[i].GetElementType(); + il.EmitLoadArg(i + 1); + il.Emit(OpCodes.Ldloc, parameters); + il.EmitInt(i); + il.Emit(OpCodes.Ldelem_Ref); + il.EmitConvertFromObject(byrefToType); + il.EmitStRef(byrefToType); + } + } + } + + if (returnValue != null) + il.Emit(OpCodes.Ldloc, returnValue); + + il.Emit(OpCodes.Ret); + } + + private void EmitInitializeMetaData(ILGenerator il, AspectActivatorBody node, MethodBuilder methodBuilder) + { + var serviceMethod = node.ServiceMethod; + var implementationMethod = node.ImplementationMethod; + + if (node.IsGeneric) + { + il.EmitMethod(serviceMethod.MakeGenericMethod(methodBuilder.GetGenericArguments())); + il.EmitMethod(implementationMethod.MakeGenericMethod(methodBuilder.GetGenericArguments())); + il.EmitMethod(methodBuilder.MakeGenericMethod(methodBuilder.GetGenericArguments())); + } + else + { + var serviceKey = $"service{serviceMethod.GetDisplayName()}"; + var implKey = $"impl{implementationMethod.GetDisplayName()}"; + var proxyKey = $"proxy{serviceMethod.GetDisplayName()}"; + + // Store the proxy method builder in constants + _ctx.MethodConstants.AddMethod(proxyKey, methodBuilder); + + _ctx.MethodConstants.LoadMethod(il, serviceKey); + _ctx.MethodConstants.LoadMethod(il, implKey); + _ctx.MethodConstants.LoadMethod(il, proxyKey); + } + + il.EmitThis(); + il.Emit(OpCodes.Ldfld, _ctx.Fields["_implementation"]); + il.EmitThis(); + + var parameterTypes = serviceMethod.GetParameterTypes(); + if (parameterTypes.Length == 0) + { + il.Emit(OpCodes.Ldnull); + return; + } + + il.EmitInt(parameterTypes.Length); + il.Emit(OpCodes.Newarr, typeof(object)); + for (var i = 0; i < parameterTypes.Length; i++) + { + il.Emit(OpCodes.Dup); + il.EmitInt(i); + il.EmitLoadArg(i + 1); + if (parameterTypes[i].IsByRef) + { + il.EmitLdRef(parameterTypes[i]); + il.EmitConvertToObject(parameterTypes[i].GetElementType()); + } + else + { + il.EmitConvertToObject(parameterTypes[i]); + } + il.Emit(OpCodes.Stelem_Ref); + } + } + + private void EmitReturnValue(ILGenerator il, AspectActivatorBody node) + { + switch (node.ReturnKind) + { + case ReturnKind.Void: + il.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvoke.MakeGenericMethod(typeof(object))); + il.Emit(OpCodes.Pop); + break; + case ReturnKind.Task: + il.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvokeTask.MakeGenericMethod(typeof(object))); + break; + case ReturnKind.TaskOfT: + var taskReturnType = node.ReturnType.GetTypeInfo().GetGenericArguments().Single(); + il.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvokeTask.MakeGenericMethod(taskReturnType)); + break; + case ReturnKind.ValueTask: + il.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvokeValueTask.MakeGenericMethod(typeof(object))); + break; + case ReturnKind.ValueTaskOfT: + var vtReturnType = node.ReturnType.GetTypeInfo().GetGenericArguments().Single(); + il.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvokeValueTask.MakeGenericMethod(vtReturnType)); + break; + case ReturnKind.Sync: + il.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvoke.MakeGenericMethod(node.ReturnType)); + break; + } + } + + public void VisitStubBody(StubBody node) + { + var il = _ctx.CurrentILGenerator; + if (node.ReturnType != typeof(void)) + il.EmitDefault(node.ReturnType); + il.Emit(OpCodes.Ret); + } + + public void VisitBackingFieldGetBody(BackingFieldGetBody node) + { + var il = _ctx.CurrentILGenerator; + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldfld, _ctx.Fields[node.FieldName]); + il.Emit(OpCodes.Ret); + } + + public void VisitBackingFieldSetBody(BackingFieldSetBody node) + { + var il = _ctx.CurrentILGenerator; + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Stfld, _ctx.Fields[node.FieldName]); + il.Emit(OpCodes.Ret); + } + + // --- Helper methods --- + + private void DefineGenericParameters(TypeBuilder typeBuilder, IReadOnlyList nodes) + { + var names = nodes.Select(n => n.Name).ToArray(); + var builders = typeBuilder.DefineGenericParameters(names); + for (int i = 0; i < nodes.Count; i++) + { + builders[i].SetGenericParameterAttributes(nodes[i].Constraints); + if (nodes[i].BaseTypeConstraint != null) + builders[i].SetBaseTypeConstraint(nodes[i].BaseTypeConstraint); + if (nodes[i].InterfaceConstraints.Length > 0) + builders[i].SetInterfaceConstraints(nodes[i].InterfaceConstraints); + foreach (var attribute in nodes[i].Attributes) + SetCustomAttribute(builders[i], attribute); + } + } + + private void DefineMethodGenericParameters(MethodBuilder methodBuilder, IReadOnlyList nodes) + { + var names = nodes.Select(n => n.Name).ToArray(); + var builders = methodBuilder.DefineGenericParameters(names); + for (int i = 0; i < nodes.Count; i++) + { + builders[i].SetGenericParameterAttributes(nodes[i].Constraints); + if (nodes[i].BaseTypeConstraint != null) + builders[i].SetBaseTypeConstraint(nodes[i].BaseTypeConstraint); + if (nodes[i].InterfaceConstraints.Length > 0) + builders[i].SetInterfaceConstraints(nodes[i].InterfaceConstraints); + foreach (var attribute in nodes[i].Attributes) + SetCustomAttribute(builders[i], attribute); + } + } + + private CustomAttributeBuilder BuildCustomAttribute(Type attributeType) + { + return new CustomAttributeBuilder( + attributeType.GetTypeInfo().GetConstructor(Type.EmptyTypes), + ArrayUtils.Empty()); + } + + private CustomAttributeBuilder BuildCustomAttribute(CustomAttributeData data) + { + if (data.NamedArguments != null) + { + var attributeTypeInfo = data.AttributeType.GetTypeInfo(); + var constructorArgs = data.ConstructorArguments.Select(ReadAttributeValue).ToArray(); + var namedProperties = data.NamedArguments.Where(n => !n.IsField).Select(n => attributeTypeInfo.GetProperty(n.MemberName)).ToArray(); + var propertyValues = data.NamedArguments.Where(n => !n.IsField).Select(n => ReadAttributeValue(n.TypedValue)).ToArray(); + var namedFields = data.NamedArguments.Where(n => n.IsField).Select(n => attributeTypeInfo.GetField(n.MemberName)).ToArray(); + var fieldValues = data.NamedArguments.Where(n => n.IsField).Select(n => ReadAttributeValue(n.TypedValue)).ToArray(); + return new CustomAttributeBuilder(data.Constructor, constructorArgs, namedProperties, propertyValues, namedFields, fieldValues); + } + + return new CustomAttributeBuilder(data.Constructor, data.ConstructorArguments.Select(c => c.Value).ToArray()); + } + + private static object ReadAttributeValue(CustomAttributeTypedArgument argument) + { + var value = argument.Value; + if (!argument.ArgumentType.GetTypeInfo().IsArray) + return value; + return ((IEnumerable)value).Select(m => m.Value).ToArray(); + } + + private void SetCustomAttribute(TypeBuilder builder, AttributeNode node) + { + builder.SetCustomAttribute(node.IsMarker ? BuildCustomAttribute(node.MarkerAttributeType) : BuildCustomAttribute(node.CustomAttributeData)); + } + + private void SetCustomAttribute(GenericTypeParameterBuilder builder, AttributeNode node) + { + builder.SetCustomAttribute(node.IsMarker ? BuildCustomAttribute(node.MarkerAttributeType) : BuildCustomAttribute(node.CustomAttributeData)); + } + + private void SetCustomAttribute(MethodBuilder builder, AttributeNode node) + { + builder.SetCustomAttribute(node.IsMarker ? BuildCustomAttribute(node.MarkerAttributeType) : BuildCustomAttribute(node.CustomAttributeData)); + } + + private void SetCustomAttribute(ConstructorBuilder builder, AttributeNode node) + { + builder.SetCustomAttribute(node.IsMarker ? BuildCustomAttribute(node.MarkerAttributeType) : BuildCustomAttribute(node.CustomAttributeData)); + } + + private void SetCustomAttribute(PropertyBuilder builder, AttributeNode node) + { + builder.SetCustomAttribute(node.IsMarker ? BuildCustomAttribute(node.MarkerAttributeType) : BuildCustomAttribute(node.CustomAttributeData)); + } + + private void SetCustomAttribute(ParameterBuilder builder, AttributeNode node) + { + builder.SetCustomAttribute(node.IsMarker ? BuildCustomAttribute(node.MarkerAttributeType) : BuildCustomAttribute(node.CustomAttributeData)); + } + + // Copied from original ParameterBuilderUtils.CopyDefaultValueConstant + private static void CopyDefaultValueConstant(ParameterNode from, ParameterBuilder to) + { + var defaultValue = from.DefaultValue; + + if (defaultValue is System.Reflection.Missing) + return; + + try + { + to.SetConstant(defaultValue); + } + catch (ArgumentException) + { + var parameterType = from.ParameterType; + var parameterNonNullableType = parameterType; + var isNullableType = parameterType.IsNullableType(); + + if (defaultValue == null) + { + if (isNullableType || parameterType.IsValueType) + return; + } + else if (isNullableType) + { + parameterNonNullableType = parameterType.GetGenericArguments()[0]; + if (parameterNonNullableType.IsEnum || parameterNonNullableType.IsInstanceOfType(defaultValue)) + return; + } + + try + { + var coercedDefaultValue = Convert.ChangeType(defaultValue, parameterNonNullableType, CultureInfo.InvariantCulture); + to.SetConstant(coercedDefaultValue); + return; + } + catch { } + + throw; + } + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitorContext.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitorContext.cs new file mode 100644 index 00000000..bedc7d6e --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/ILEmitVisitorContext.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using AspectCore.Extensions.Reflection.Emit; + +namespace AspectCore.DynamicProxy.ProxyBuilder.Visitors +{ + internal class ILEmitVisitorContext + { + public ModuleBuilder ModuleBuilder { get; } + + public TypeBuilder TypeBuilder { get; set; } + + public Type ServiceType { get; set; } + + public Dictionary Fields { get; } = new Dictionary(); + + public MethodConstantTable MethodConstants { get; set; } + + public ILGenerator CurrentILGenerator { get; set; } + + public MethodBuilder CurrentMethodBuilder { get; set; } + + public ILEmitVisitorContext(ModuleBuilder moduleBuilder) + { + ModuleBuilder = moduleBuilder ?? throw new ArgumentNullException(nameof(moduleBuilder)); + } + } + + internal class MethodConstantTable + { + private readonly TypeBuilder _nestedTypeBuilder; + private readonly ConstructorBuilder _constructorBuilder; + private readonly ILGenerator _ilGen; + private readonly Dictionary _fields; + + public MethodConstantTable(TypeBuilder typeBuilder) + { + _fields = new Dictionary(); + _nestedTypeBuilder = typeBuilder.DefineNestedType("MethodConstant", TypeAttributes.NestedPrivate); + _constructorBuilder = _nestedTypeBuilder.DefineTypeInitializer(); + _ilGen = _constructorBuilder.GetILGenerator(); + } + + public void AddMethod(string name, MethodInfo method) + { + if (!_fields.ContainsKey(name)) + { + var field = _nestedTypeBuilder.DefineField(name, typeof(MethodInfo), FieldAttributes.Static | FieldAttributes.InitOnly | FieldAttributes.Assembly); + _fields.Add(name, field); + if (method != null) + { + _ilGen.EmitMethod(method); + _ilGen.Emit(OpCodes.Stsfld, field); + } + } + } + + public void LoadMethod(ILGenerator ilGen, string name) + { + if (_fields.TryGetValue(name, out FieldBuilder field)) + { + ilGen.Emit(OpCodes.Ldsfld, field); + return; + } + throw new InvalidOperationException($"Failed to find the method associated with the specified key {name}."); + } + + public void Compile() + { + _ilGen.Emit(OpCodes.Ret); + _nestedTypeBuilder.CreateTypeInfo(); + } + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/IProxyAstVisitor.cs b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/IProxyAstVisitor.cs new file mode 100644 index 00000000..ad5c279b --- /dev/null +++ b/src/AspectCore.Core/DynamicProxy/ProxyBuilder/Visitors/IProxyAstVisitor.cs @@ -0,0 +1,38 @@ +using System; +using AspectCore.DynamicProxy.ProxyBuilder.Nodes; + +namespace AspectCore.DynamicProxy.ProxyBuilder +{ + internal interface IProxyBuilderVisitor + { + Type VisitProxyType(ProxyTypeNode node); + + void VisitField(FieldNode node); + + void VisitConstructor(ConstructorNode node); + + void VisitMethod(MethodNode node); + + void VisitProperty(PropertyNode node); + + void VisitParameter(ParameterNode node); + + void VisitGenericParameter(GenericParameterNode node); + + void VisitAttribute(AttributeNode node); + + void VisitMethodConstant(MethodConstantNode node); + + void VisitDirectDelegationBody(DirectDelegationBody node); + + void VisitReflectorDelegationBody(ReflectorDelegationBody node); + + void VisitAspectActivatorBody(AspectActivatorBody node); + + void VisitStubBody(StubBody node); + + void VisitBackingFieldGetBody(BackingFieldGetBody node); + + void VisitBackingFieldSetBody(BackingFieldSetBody node); + } +} diff --git a/src/AspectCore.Core/DynamicProxy/ProxyTypeGenerator.cs b/src/AspectCore.Core/DynamicProxy/ProxyTypeGenerator.cs index 2c12af38..50c81f17 100644 --- a/src/AspectCore.Core/DynamicProxy/ProxyTypeGenerator.cs +++ b/src/AspectCore.Core/DynamicProxy/ProxyTypeGenerator.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using AspectCore.Utils; +using AspectCore.DynamicProxy.ProxyBuilder; using AspectCore.Extensions.Reflection; namespace AspectCore.DynamicProxy @@ -11,7 +11,7 @@ namespace AspectCore.DynamicProxy public sealed class ProxyTypeGenerator : IProxyTypeGenerator { private readonly IAspectValidator _aspectValidator; - private readonly ProxyGeneratorUtils _proxyGeneratorUtils; + private readonly ProxyTypeCompiler _proxyGeneratorUtils; public ProxyTypeGenerator(IAspectValidatorBuilder aspectValidatorBuilder) { @@ -20,7 +20,7 @@ public ProxyTypeGenerator(IAspectValidatorBuilder aspectValidatorBuilder) throw new ArgumentNullException(nameof(aspectValidatorBuilder)); } _aspectValidator = aspectValidatorBuilder.Build(); - _proxyGeneratorUtils = new ProxyGeneratorUtils(); + _proxyGeneratorUtils = new ProxyTypeCompiler(); } public Type CreateClassProxyType(Type serviceType, Type implementationType) diff --git a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs b/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs deleted file mode 100644 index 0c635387..00000000 --- a/src/AspectCore.Core/Utils/ProxyGeneratorUtils.cs +++ /dev/null @@ -1,1304 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using System.Threading; -using System.Threading.Tasks; -using AspectCore.DynamicProxy; -using AspectCore.Extensions.Reflection; -using AspectCore.Extensions.Reflection.Emit; - -namespace AspectCore.Utils -{ - internal class ProxyGeneratorUtils - { - private const string ProxyNameSpace = "AspectCore.DynamicGenerated"; - private const string ProxyAssemblyName = "AspectCore.DynamicProxy.Generator"; - private readonly ModuleBuilder _moduleBuilder; - private readonly Dictionary _definedTypes; - private readonly object _lock = new object(); - private readonly ProxyNameUtils _proxyNameUtils; - - public ProxyGeneratorUtils() - { - var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(ProxyAssemblyName), AssemblyBuilderAccess.RunAndCollect); - _moduleBuilder = asmBuilder.DefineDynamicModule("core"); - _definedTypes = new Dictionary(); - _proxyNameUtils = new ProxyNameUtils(); - } - - internal Type CreateInterfaceProxy(Type interfaceType, Type[] additionalInterfaces, IAspectValidator aspectValidator) - { - if (!interfaceType.GetTypeInfo().IsVisible() || !interfaceType.GetTypeInfo().IsInterface) - { - throw new InvalidOperationException($"Validate '{interfaceType}' failed because the type does not satisfy the visible conditions."); - } - - lock (_lock) - { - var name = _proxyNameUtils.GetInterfaceImplTypeFullName(interfaceType); - if (!_definedTypes.TryGetValue(name, out Type type)) - { - type = CreateInterfaceImplInternal(name, interfaceType, additionalInterfaces, aspectValidator); - _definedTypes[name] = type; - } - return type; - } - } - - internal Type CreateInterfaceProxy(Type interfaceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator) - { - if (!interfaceType.GetTypeInfo().IsVisible() || !interfaceType.GetTypeInfo().IsInterface) - { - throw new InvalidOperationException($"Validate '{interfaceType}' failed because the type does not satisfy the visible conditions."); - } - - lock (_lock) - { - var name = _proxyNameUtils.GetProxyTypeName(interfaceType, implType); - if (!_definedTypes.TryGetValue(name, out Type type)) - { - type = CreateInterfaceProxyInternal(name, interfaceType, implType, additionalInterfaces, aspectValidator); - _definedTypes[name] = type; - } - return type; - } - } - - internal Type CreateClassProxy(Type serviceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator) - { - if (!serviceType.GetTypeInfo().IsVisible() || !serviceType.GetTypeInfo().IsClass) - { - throw new InvalidOperationException($"Validate '{serviceType}' failed because the type does not satisfy the visible conditions."); - } - if (!implType.GetTypeInfo().CanInherited()) - { - throw new InvalidOperationException($"Validate '{implType}' failed because the type does not satisfy the condition to be inherited."); - } - - lock (_lock) - { - var name = _proxyNameUtils.GetProxyTypeName(serviceType, implType); - if (!_definedTypes.TryGetValue(name, out Type type)) - { - type = CreateClassProxyInternal(name, serviceType, implType, additionalInterfaces, aspectValidator); - _definedTypes[name] = type; - } - return type; - } - } - - private Type CreateInterfaceImplInternal(string name, Type interfaceType, Type[] additionalInterfaces, IAspectValidator aspectValidator) - { - var interfaceTypes = new Type[] { interfaceType }.Concat(additionalInterfaces).Distinct().ToArray(); - var implTypeBuilder = _moduleBuilder.DefineType(name, TypeAttributes.Public, typeof(object), interfaceTypes); - - GenericParameterUtils.DefineGenericParameter(interfaceType, implTypeBuilder); - - ConstructorBuilderUtils.DefineInterfaceImplConstructor(implTypeBuilder); - - MethodBuilderUtils.DefineInterfaceImplMethods(interfaceTypes, implTypeBuilder); - - PropertyBuilderUtils.DefineInterfaceImplProperties(interfaceTypes, implTypeBuilder); - - var implType = implTypeBuilder.CreateTypeInfo().AsType(); - - var typeDesc = TypeBuilderUtils.DefineType(_moduleBuilder, _proxyNameUtils.GetProxyTypeName(_proxyNameUtils.GetInterfaceImplTypeName(interfaceType), interfaceType, implType), - interfaceType, typeof(object), interfaceTypes); - - typeDesc.Properties[nameof(IAspectValidator)] = aspectValidator; - - //define constructor - ConstructorBuilderUtils.DefineInterfaceProxyConstructor(interfaceType, implType, typeDesc); - //define methods - MethodBuilderUtils.DefineInterfaceProxyMethods(interfaceType, implType, additionalInterfaces, typeDesc); - - PropertyBuilderUtils.DefineInterfaceProxyProperties(interfaceType, implType, additionalInterfaces, typeDesc); - - return typeDesc.Compile(); - } - - private Type CreateInterfaceProxyInternal(string name, Type interfaceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator) - { - var interfaces = new Type[] { interfaceType }.Concat(additionalInterfaces).Distinct().ToArray(); - - var typeDesc = TypeBuilderUtils.DefineType(_moduleBuilder, name, interfaceType, typeof(object), interfaces); - - typeDesc.Properties[nameof(IAspectValidator)] = aspectValidator; - - //define constructor - ConstructorBuilderUtils.DefineInterfaceProxyConstructor(interfaceType, typeDesc); - - //define methods - MethodBuilderUtils.DefineInterfaceProxyMethods(interfaceType, implType, additionalInterfaces, typeDesc); - - PropertyBuilderUtils.DefineInterfaceProxyProperties(interfaceType, implType, additionalInterfaces, typeDesc); - - return typeDesc.Compile(); - } - - private Type CreateClassProxyInternal(string name, Type serviceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator) - { - var interfaces = additionalInterfaces.Distinct().ToArray(); - - var typeDesc = TypeBuilderUtils.DefineType(_moduleBuilder, name, serviceType, implType, interfaces); - - TypeBuilderUtils.DefineTypeAttributes(implType, typeDesc); - - typeDesc.Properties[nameof(IAspectValidator)] = aspectValidator; - - //define constructor - ConstructorBuilderUtils.DefineClassProxyConstructors(serviceType, implType, typeDesc); - - //define methods - MethodBuilderUtils.DefineClassProxyMethods(serviceType, implType, additionalInterfaces, typeDesc); - - PropertyBuilderUtils.DefineClassProxyProperties(serviceType, implType, additionalInterfaces, typeDesc); - - return typeDesc.Compile(); - } - - private class ProxyNameUtils - { - private readonly Dictionary _indexs = new Dictionary(); - private readonly Dictionary, string> _indexMaps = new Dictionary, string>(); - - private string GetProxyTypeIndex(string className, Type serviceType, Type implementationType) - { - ProxyNameIndex nameIndex; - if (!_indexs.TryGetValue(className, out nameIndex)) - { - nameIndex = new ProxyNameIndex(); - _indexs[className] = nameIndex; - } - var key = Tuple.Create(serviceType, implementationType); - string index; - if (!_indexMaps.TryGetValue(key, out index)) - { - var tempIndex = nameIndex.GenIndex(); - index = tempIndex == 0 ? string.Empty : tempIndex.ToString(); - _indexMaps[key] = index; - } - Debug.WriteLine($"{className}-{serviceType}-{implementationType}-{index}"); - return index; - } - - public string GetInterfaceImplTypeName(Type interfaceType) - { - var className = interfaceType.GetReflector().DisplayName; - if (className.StartsWith("I", StringComparison.Ordinal)) - { - className = className.Substring(1); - } - return className /*+ "Impl"*/; - } - - public string GetInterfaceImplTypeFullName(Type interfaceType) - { - var className = GetInterfaceImplTypeName(interfaceType); - return $"{ProxyNameSpace}.{className}{GetProxyTypeIndex(className, interfaceType, interfaceType)}"; - } - - public string GetProxyTypeName(Type serviceType, Type implType) - { - return $"{ProxyNameSpace}.{implType.GetReflector().DisplayName}{GetProxyTypeIndex(implType.GetReflector().DisplayName, serviceType, implType)}"; - } - - public string GetProxyTypeName(string className, Type serviceType, Type implType) - { - return $"{ProxyNameSpace}.{className}{GetProxyTypeIndex(className, serviceType, implType)}"; - } - } - - private class ProxyNameIndex - { - private int _index = -1; - - public int GenIndex() - { - return Interlocked.Increment(ref _index); - } - } - - private class TypeBuilderUtils - { - public static TypeDesc DefineType(ModuleBuilder moduleBuilder, string name, Type serviceType, Type parentType, Type[] interfaces) - { - //define proxy type for interface service - var typeBuilder = moduleBuilder.DefineType(name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, parentType, interfaces); - - //define genericParameter - GenericParameterUtils.DefineGenericParameter(serviceType, typeBuilder); - - //define default attribute - typeBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(NonAspectAttribute))); - typeBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - - //define private field - var fieldTable = FieldBuilderUtils.DefineFields(serviceType, typeBuilder); - - return new TypeDesc(serviceType, typeBuilder, fieldTable, new MethodConstantTable(typeBuilder)); - } - - public static void DefineTypeAttributes(Type implType, TypeDesc typeDesc) - { - //inherit implement type's attributes - foreach (var customAttributeData in implType.CustomAttributes) - { - typeDesc.Builder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); - } - } - } - - private class ConstructorBuilderUtils - { - internal static void DefineInterfaceImplConstructor(TypeBuilder typeBuilder) - { - var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, MethodUtils.ObjectCtor.CallingConvention, Type.EmptyTypes); - var ilGen = constructorBuilder.GetILGenerator(); - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Call, MethodUtils.ObjectCtor); - ilGen.Emit(OpCodes.Ret); - } - - internal static void DefineInterfaceProxyConstructor(Type interfaceType, Type implType, TypeDesc typeDesc) - { - var constructorBuilder = typeDesc.Builder.DefineConstructor(MethodAttributes.Public, MethodUtils.ObjectCtor.CallingConvention, new Type[] { typeof(IAspectActivatorFactory) }); - - constructorBuilder.DefineParameter(1, ParameterAttributes.None, FieldBuilderUtils.ActivatorFactory); - - var ilGen = constructorBuilder.GetILGenerator(); - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Call, MethodUtils.ObjectCtor); - - ilGen.EmitThis(); - ilGen.EmitLoadArg(1); - ilGen.Emit(OpCodes.Stfld, typeDesc.Fields[FieldBuilderUtils.ActivatorFactory]); - - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Newobj, implType.GetTypeInfo().GetConstructor(Type.EmptyTypes)); - ilGen.Emit(OpCodes.Stfld, typeDesc.Fields[FieldBuilderUtils.Target]); - - ilGen.Emit(OpCodes.Ret); - } - - internal static void DefineInterfaceProxyConstructor(Type interfaceType, TypeDesc typeDesc) - { - var constructorBuilder = typeDesc.Builder.DefineConstructor(MethodAttributes.Public, MethodUtils.ObjectCtor.CallingConvention, new Type[] { typeof(IAspectActivatorFactory), interfaceType }); - - constructorBuilder.DefineParameter(1, ParameterAttributes.None, FieldBuilderUtils.ActivatorFactory); - constructorBuilder.DefineParameter(2, ParameterAttributes.None, FieldBuilderUtils.Target); - - var ilGen = constructorBuilder.GetILGenerator(); - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Call, MethodUtils.ObjectCtor); - - ilGen.EmitThis(); - ilGen.EmitLoadArg(1); - ilGen.Emit(OpCodes.Stfld, typeDesc.Fields[FieldBuilderUtils.ActivatorFactory]); - - ilGen.EmitThis(); - ilGen.EmitLoadArg(2); - ilGen.Emit(OpCodes.Stfld, typeDesc.Fields[FieldBuilderUtils.Target]); - - ilGen.Emit(OpCodes.Ret); - } - - internal static void DefineClassProxyConstructors(Type serviceType, Type implType, TypeDesc typeDesc) - { - - var constructors = implType.GetTypeInfo().DeclaredConstructors.Where(c => !c.IsStatic && (c.IsPublic || c.IsFamily || c.IsFamilyAndAssembly || c.IsFamilyOrAssembly)).ToArray(); - if (constructors.Length == 0) - { - throw new InvalidOperationException( - $"A suitable constructor for type {serviceType.FullName} could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor."); - } - foreach (var constructor in constructors) - { - var parameterTypes = constructor.GetParameters().Select(p => p.ParameterType).ToArray(); - var parameters = new Type[] { typeof(IAspectActivatorFactory) }.Concat(parameterTypes).ToArray(); - var constructorBuilder = typeDesc.Builder.DefineConstructor(constructor.Attributes, constructor.CallingConvention, parameters); - - constructorBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - - //inherit constructor's attribute - foreach (var customAttributeData in constructor.CustomAttributes) - { - constructorBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); - } - - ParameterBuilderUtils.DefineParameters(constructor, constructorBuilder); - - var ilGen = constructorBuilder.GetILGenerator(); - - ilGen.EmitThis(); - ilGen.EmitLoadArg(1); - ilGen.Emit(OpCodes.Stfld, typeDesc.Fields[FieldBuilderUtils.ActivatorFactory]); - - ilGen.EmitThis(); - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Stfld, typeDesc.Fields[FieldBuilderUtils.Target]); - - ilGen.EmitThis(); - for (var i = 2; i <= parameters.Length; i++) - { - ilGen.EmitLoadArg(i); - } - ilGen.Emit(OpCodes.Call, constructor); - - ilGen.Emit(OpCodes.Ret); - } - } - } - - private class MethodBuilderUtils - { - const MethodAttributes ExplicitMethodAttributes = MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; - internal const MethodAttributes InterfaceMethodAttributes = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual; - const MethodAttributes OverrideMethodAttributes = MethodAttributes.HideBySig | MethodAttributes.Virtual; - private static readonly HashSet ignores = new HashSet { "Finalize" }; - - internal static void DefineInterfaceImplMethods(Type[] interfaceTypes, TypeBuilder implTypeBuilder) - { - foreach (var item in interfaceTypes) - { - foreach (var method in item.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) - { - DefineInterfaceImplMethod(method, implTypeBuilder); - } - } - } - - internal static MethodBuilder DefineInterfaceImplMethod(MethodInfo method, TypeBuilder implTypeBuilder) - { - // method is not abstract means it is a default implementation on the interface, so don't need to define method on the proxy type. - if (method.IsAbstract == false) - return null; - - var methodBuilder = implTypeBuilder.DefineMethod(method.Name, InterfaceMethodAttributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes()); - var ilGen = methodBuilder.GetILGenerator(); - if (method.ReturnType != typeof(void)) - { - ilGen.EmitDefault(method.ReturnType); - } - ilGen.Emit(OpCodes.Ret); - implTypeBuilder.DefineMethodOverride(methodBuilder, method); - return methodBuilder; - } - - internal static void DefineInterfaceProxyMethods(Type interfaceType, Type targetType, Type[] additionalInterfaces, TypeDesc typeDesc) - { - foreach (var method in interfaceType.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) - { - DefineInterfaceMethod(method, targetType, typeDesc); - } - foreach (var item in additionalInterfaces) - { - foreach (var method in item.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) - { - DefineExplicitMethod(method, targetType, typeDesc); - } - } - } - - internal static void DefineClassProxyMethods(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) - { - foreach (var method in serviceType.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => !x.IsPropertyBinding())) - { - if (method.IsVisibleAndVirtual() && !ignores.Contains(method.Name)) - DefineClassMethod(method, implType, typeDesc); - } - foreach (var item in additionalInterfaces) - { - foreach (var method in item.GetTypeInfo().DeclaredMethods.Where(x => !x.IsPropertyBinding())) - { - DefineExplicitMethod(method, implType, typeDesc); - } - } - } - - internal static MethodBuilder DefineInterfaceMethod(MethodInfo method, Type implType, TypeDesc typeDesc) - { - var methodBuilder = DefineMethod(method, method.Name, InterfaceMethodAttributes, implType, typeDesc); - typeDesc.Builder.DefineMethodOverride(methodBuilder, method); - return methodBuilder; - } - - internal static MethodBuilder DefineExplicitMethod(MethodInfo method, Type implType, TypeDesc typeDesc) - { - var methodBuilder = DefineMethod(method, method.GetName(), ExplicitMethodAttributes, implType, typeDesc); - typeDesc.Builder.DefineMethodOverride(methodBuilder, method); - return methodBuilder; - } - - internal static MethodBuilder DefineClassMethod(MethodInfo method, Type implType, TypeDesc typeDesc) - { - var attributes = OverrideMethodAttributes; - - if (method.Attributes.HasFlag(MethodAttributes.Public)) - { - attributes = attributes | MethodAttributes.Public; - } - - if (method.Attributes.HasFlag(MethodAttributes.Family)) - { - attributes = attributes | MethodAttributes.Family; - } - - if (method.Attributes.HasFlag(MethodAttributes.FamORAssem)) - { - attributes = attributes | MethodAttributes.FamORAssem; - } - - var methodBuilder = DefineMethod(method, method.Name, attributes, implType, typeDesc); - return methodBuilder; - } - - private static MethodBuilder DefineMethod(MethodInfo method, string name, MethodAttributes attributes, Type implType, TypeDesc typeDesc) - { - var methodBuilder = typeDesc.Builder.DefineMethod(name, attributes, method.CallingConvention, method.ReturnType, method.GetParameterTypes()); - - GenericParameterUtils.DefineGenericParameter(method, methodBuilder); - - //define method attributes - methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - - //inherit targetMethod's attribute - foreach (var customAttributeData in method.CustomAttributes) - { - methodBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); - } - - //define paramters - ParameterBuilderUtils.DefineParameters(method, methodBuilder); - - var implementationMethod = implType.GetTypeInfo().GetMethodBySignature(method); - if (implementationMethod == null) - { - var interfaces = implType.GetInterfaces(); - if (interfaces == null || interfaces.Length <= 0) - { - throw new MissingMethodException($"Type '{implType}' does not contain a method '{method}'."); - } - var @interface = interfaces.Where(f => f.GetCustomAttribute(typeof(AbstractInterceptorAttribute)) != null).ToArray(); - if (@interface.Length > 0) - { - foreach (var item in @interface) - { - implementationMethod = item.GetTypeInfo().GetMethodBySignature(method); - if (implementationMethod != null) break; - } - } - else - { - foreach (var item in interfaces) - { - implementationMethod = item.GetTypeInfo().GetMethodBySignature(method); - if (implementationMethod != null) break; - } - } - if (implementationMethod == null) - { - throw new MissingMethodException($"Type '{implType}' does not contain a method '{method}'."); - } - } - - if (method.IsNonAspect()) - { - EmitMethodBody(); - } - else if (typeDesc.GetProperty().Validate(method, true) || typeDesc.GetProperty().Validate(implementationMethod, false)) - { - EmitProxyMethodBody(); - } - else - { - EmitMethodBody(); - } - - return methodBuilder; - - void EmitMethodBody() - { - var ilGen = methodBuilder.GetILGenerator(); - var parameters = method.GetParameterTypes(); - if (typeDesc.ServiceType.GetTypeInfo().IsInterface || !implementationMethod.IsExplicit()) - { - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Ldfld, typeDesc.Fields[FieldBuilderUtils.Target]); - for (int i = 1; i <= parameters.Length; i++) - { - ilGen.EmitLoadArg(i); - } - var callOpCode = implementationMethod.IsCallvirt() ? OpCodes.Callvirt : OpCodes.Call; - ilGen.Emit(callOpCode, implementationMethod.IsExplicit() ? method : implementationMethod); - } - else - { - var reflectorLocal = ilGen.DeclareLocal(typeof(MethodReflector)); - var argsLocal = ilGen.DeclareLocal(typeof(object[])); - var returnLocal = ilGen.DeclareLocal(typeof(object)); - ilGen.EmitMethod(implementationMethod); - ilGen.Emit(OpCodes.Call, MethodUtils.GetMethodReflector); - ilGen.Emit(OpCodes.Stloc, reflectorLocal); - ilGen.EmitInt(parameters.Length); - ilGen.Emit(OpCodes.Newarr, typeof(object)); - for (var i = 0; i < parameters.Length; i++) - { - ilGen.Emit(OpCodes.Dup); - ilGen.EmitInt(i); - ilGen.EmitLoadArg(i + 1); - if (parameters[i].IsByRef) - { - ilGen.EmitLdRef(parameters[i]); - ilGen.EmitConvertToObject(parameters[i].GetElementType()); - } - else - { - ilGen.EmitConvertToObject(parameters[i]); - } - ilGen.Emit(OpCodes.Stelem_Ref); - } - ilGen.Emit(OpCodes.Stloc, argsLocal); - ilGen.Emit(OpCodes.Ldloc, reflectorLocal); - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Ldloc, argsLocal); - ilGen.Emit(OpCodes.Callvirt, MethodUtils.ReflectorInvoke); - ilGen.Emit(OpCodes.Stloc, returnLocal); - for (var i = 0; i < parameters.Length; i++) - { - if (parameters[i].IsByRef) - { - Type byrefToType = parameters[i].GetElementType(); - - ilGen.EmitLoadArg(i + 1); - ilGen.Emit(OpCodes.Ldloc, argsLocal); - ilGen.EmitInt(i); - ilGen.Emit(OpCodes.Ldelem_Ref); - ilGen.EmitConvertFromObject(parameters[i].GetElementType()); - ilGen.EmitStRef(byrefToType); - } - } - if (!method.IsVoid()) - { - ilGen.Emit(OpCodes.Ldloc, returnLocal); - ilGen.EmitConvertFromObject(method.ReturnType); - } - } - ilGen.Emit(OpCodes.Ret); - } - - void EmitProxyMethodBody() - { - var ilGen = methodBuilder.GetILGenerator(); - var activatorContext = ilGen.DeclareLocal(typeof(AspectActivatorContext)); - var returnValue = default(LocalBuilder); - - EmitInitializeMetaData(ilGen); - - ilGen.Emit(OpCodes.Newobj, MethodUtils.AspectActivatorContextCtor); - ilGen.Emit(OpCodes.Stloc, activatorContext); - - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Ldfld, typeDesc.Fields[FieldBuilderUtils.ActivatorFactory]); - ilGen.Emit(OpCodes.Callvirt, MethodUtils.CreateAspectActivator); - ilGen.Emit(OpCodes.Ldloc, activatorContext); - - EmitReturnVaule(ilGen); - - if (method.ReturnType != typeof(void)) - { - returnValue = ilGen.DeclareLocal(method.ReturnType); - ilGen.Emit(OpCodes.Stloc, returnValue); - } - - var parameterTypes = method.GetParameterTypes(); - - if (parameterTypes.Any(x => x.IsByRef)) - { - var parameters = ilGen.DeclareLocal(typeof(object[])); - ilGen.Emit(OpCodes.Ldloca, activatorContext); - ilGen.Emit(OpCodes.Call, MethodUtils.GetParameters); - ilGen.Emit(OpCodes.Stloc, parameters); - for (var i = 0; i < parameterTypes.Length; i++) - { - if (parameterTypes[i].IsByRef) - { - Type byrefToType = parameterTypes[i].GetElementType(); - - ilGen.EmitLoadArg(i + 1); - ilGen.Emit(OpCodes.Ldloc, parameters); - ilGen.EmitInt(i); - ilGen.Emit(OpCodes.Ldelem_Ref); - ilGen.EmitConvertFromObject(byrefToType); - ilGen.EmitStRef(byrefToType); - } - } - } - - if (returnValue != null) - { - ilGen.Emit(OpCodes.Ldloc, returnValue); - } - ilGen.Emit(OpCodes.Ret); - } - - void EmitInitializeMetaData(ILGenerator ilGen) - { - var serviceMethod = method; - - var methodConstants = typeDesc.MethodConstants; - - if (method.IsGenericMethodDefinition) - { - ilGen.EmitMethod(serviceMethod.MakeGenericMethod(methodBuilder.GetGenericArguments())); - ilGen.EmitMethod(implementationMethod.MakeGenericMethod(methodBuilder.GetGenericArguments())); - ilGen.EmitMethod(methodBuilder.MakeGenericMethod(methodBuilder.GetGenericArguments())); - } - else - { - methodConstants.AddMethod($"service{serviceMethod.GetDisplayName()}", serviceMethod); - methodConstants.AddMethod($"impl{implementationMethod.GetDisplayName()}", implementationMethod); - methodConstants.AddMethod($"proxy{serviceMethod.GetDisplayName()}", methodBuilder); - - methodConstants.LoadMethod(ilGen, $"service{serviceMethod.GetDisplayName()}"); - methodConstants.LoadMethod(ilGen, $"impl{implementationMethod.GetDisplayName()}"); - methodConstants.LoadMethod(ilGen, $"proxy{serviceMethod.GetDisplayName()}"); - } - - ilGen.EmitThis(); - ilGen.Emit(OpCodes.Ldfld, typeDesc.Fields[FieldBuilderUtils.Target]); - ilGen.EmitThis(); - var parameterTypes = method.GetParameterTypes(); - if (parameterTypes.Length == 0) - { - ilGen.Emit(OpCodes.Ldnull); - return; - } - ilGen.EmitInt(parameterTypes.Length); - ilGen.Emit(OpCodes.Newarr, typeof(object)); - for (var i = 0; i < parameterTypes.Length; i++) - { - ilGen.Emit(OpCodes.Dup); - ilGen.EmitInt(i); - ilGen.EmitLoadArg(i + 1); - if (parameterTypes[i].IsByRef) - { - ilGen.EmitLdRef(parameterTypes[i]); - ilGen.EmitConvertToObject(parameterTypes[i].GetElementType()); - } - else - { - ilGen.EmitConvertToObject(parameterTypes[i]); - } - ilGen.Emit(OpCodes.Stelem_Ref); - } - } - - void EmitReturnVaule(ILGenerator ilGen) - { - if (method.ReturnType == typeof(void)) - { - ilGen.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvoke.MakeGenericMethod(typeof(object))); - ilGen.Emit(OpCodes.Pop); - } - else if (method.ReturnType == typeof(Task)) - { - ilGen.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvokeTask.MakeGenericMethod(typeof(object))); - } - else if (method.IsReturnTask()) - { - var returnType = method.ReturnType.GetTypeInfo().GetGenericArguments().Single(); - ilGen.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvokeTask.MakeGenericMethod(returnType)); - } - else if (method.IsReturnValueTask()) - { - var returnType = method.ReturnType.GetTypeInfo().GetGenericArguments().Single(); - ilGen.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvokeValueTask.MakeGenericMethod(returnType)); - } - else if (method.ReturnType == typeof(ValueTask)) - { - ilGen.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvokeValueTask.MakeGenericMethod(typeof(object))); - } - else - { - ilGen.Emit(OpCodes.Callvirt, MethodUtils.AspectActivatorInvoke.MakeGenericMethod(method.ReturnType)); - } - } - } - } - - private class PropertyBuilderUtils - { - public static void DefineInterfaceProxyProperties(Type interfaceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) - { - foreach (var property in interfaceType.GetTypeInfo().DeclaredProperties) - { - var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); - DefineInterfacePropertyMethod(builder, property, implType, typeDesc); - } - foreach (var item in additionalInterfaces) - { - foreach (var property in item.GetTypeInfo().DeclaredProperties) - { - var builder = DefineInterfaceProxyProperty(property, property.GetDisplayName(), implType, typeDesc); - DefineExplicitPropertyMethod(builder, property, implType, typeDesc); - } - } - } - - internal static void DefineClassProxyProperties(Type serviceType, Type implType, Type[] additionalInterfaces, TypeDesc typeDesc) - { - foreach (var property in serviceType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) - { - if (property.IsVisibleAndVirtual()) - { - var builder = DefineInterfaceProxyProperty(property, property.Name, implType, typeDesc); - DefineClassPropertyMethod(builder, property, implType, typeDesc); - } - } - foreach (var item in additionalInterfaces) - { - foreach (var property in item.GetTypeInfo().DeclaredProperties) - { - var builder = DefineInterfaceProxyProperty(property, property.GetDisplayName(), implType, typeDesc); - DefineExplicitPropertyMethod(builder, property, implType, typeDesc); - } - } - } - - private static void DefineClassPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc) - { - if (property.CanRead) - { - var method = MethodBuilderUtils.DefineClassMethod(property.GetMethod, implType, typeDesc); - propertyBuilder.SetGetMethod(method); - } - if (property.CanWrite) - { - var method = MethodBuilderUtils.DefineClassMethod(property.SetMethod, implType, typeDesc); - propertyBuilder.SetSetMethod(method); - } - } - - private static void DefineInterfacePropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc) - { - if (property.CanRead) - { - var method = MethodBuilderUtils.DefineInterfaceMethod(property.GetMethod, implType, typeDesc); - propertyBuilder.SetGetMethod(method); - } - if (property.CanWrite) - { - var method = MethodBuilderUtils.DefineInterfaceMethod(property.SetMethod, implType, typeDesc); - propertyBuilder.SetSetMethod(method); - } - } - - private static void DefineExplicitPropertyMethod(PropertyBuilder propertyBuilder, PropertyInfo property, Type implType, TypeDesc typeDesc) - { - if (property.CanRead) - { - var method = MethodBuilderUtils.DefineExplicitMethod(property.GetMethod, implType, typeDesc); - propertyBuilder.SetGetMethod(method); - } - if (property.CanWrite) - { - var method = MethodBuilderUtils.DefineExplicitMethod(property.SetMethod, implType, typeDesc); - propertyBuilder.SetSetMethod(method); - } - } - - private static PropertyBuilder DefineInterfaceProxyProperty(PropertyInfo property, string name, Type implType, TypeDesc typeDesc) - { - var propertyBuilder = typeDesc.Builder.DefineProperty(name, property.Attributes, property.PropertyType, Type.EmptyTypes); - - propertyBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - - //inherit targetMethod's attribute - foreach (var customAttributeData in property.CustomAttributes) - { - propertyBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); - } - - return propertyBuilder; - } - - internal static void DefineInterfaceImplProperties(Type[] interfaceTypes, TypeBuilder implTypeBuilder) - { - foreach (var item in interfaceTypes) - { - foreach (var property in item.GetTypeInfo().DeclaredProperties) - { - DefineInterfaceImplProperty(property, implTypeBuilder); - } - } - } - - private static void DefineInterfaceImplProperty(PropertyInfo property, TypeBuilder implTypeBuilder) - { - var propertyBuilder = implTypeBuilder.DefineProperty(property.Name, property.Attributes, property.PropertyType, Type.EmptyTypes); - var field = implTypeBuilder.DefineField($"<{property.Name}>k__BackingField", property.PropertyType, FieldAttributes.Private); - if (property.CanRead) - { - var methodBuilder = implTypeBuilder.DefineMethod(property.GetMethod.Name, MethodBuilderUtils.InterfaceMethodAttributes, property.GetMethod.CallingConvention, property.GetMethod.ReturnType, property.GetMethod.GetParameterTypes()); - var ilGen = methodBuilder.GetILGenerator(); - ilGen.Emit(OpCodes.Ldarg_0); - ilGen.Emit(OpCodes.Ldfld, field); - ilGen.Emit(OpCodes.Ret); - implTypeBuilder.DefineMethodOverride(methodBuilder, property.GetMethod); - propertyBuilder.SetGetMethod(methodBuilder); - } - if (property.CanWrite) - { - var methodBuilder = implTypeBuilder.DefineMethod(property.SetMethod.Name, MethodBuilderUtils.InterfaceMethodAttributes, property.SetMethod.CallingConvention, property.SetMethod.ReturnType, property.SetMethod.GetParameterTypes()); - var ilGen = methodBuilder.GetILGenerator(); - ilGen.Emit(OpCodes.Ldarg_0); - ilGen.Emit(OpCodes.Ldarg_1); - ilGen.Emit(OpCodes.Stfld, field); - ilGen.Emit(OpCodes.Ret); - implTypeBuilder.DefineMethodOverride(methodBuilder, property.SetMethod); - propertyBuilder.SetSetMethod(methodBuilder); - } - - foreach (var customAttributeData in property.CustomAttributes) - { - propertyBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(customAttributeData)); - } - } - } - - private class ParameterBuilderUtils - { - public static void DefineParameters(MethodInfo targetMethod, MethodBuilder methodBuilder) - { - var parameters = targetMethod.GetParameters(); - if (parameters.Length > 0) - { - const int paramOffset = 1; // 1 - foreach (var parameter in parameters) - { - var parameterBuilder = methodBuilder.DefineParameter(parameter.Position + paramOffset, parameter.Attributes, parameter.Name); - // if (parameter.HasDefaultValue) // parameter.HasDefaultValue will throw a FormatException when parameter is DateTime type with default value - if (parameter.HasDefaultValueByAttributes()) - { - // if (!(parameter.ParameterType.GetTypeInfo().IsValueType && parameter.DefaultValue == null)) - // we can comment above line safely, and CopyDefaultValueConstant will handle this case. - // parameter.DefaultValue will throw a FormatException when parameter is DateTime type with default value - { - // parameterBuilder.SetConstant(parameter.DefaultValue); - try - { - CopyDefaultValueConstant(from: parameter, to: parameterBuilder); - } - catch - { - // Default value replication is a nice-to-have feature but not essential, - // so if it goes wrong for one parameter, just continue. - } - } - } - parameterBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - foreach (var attribute in parameter.CustomAttributes) - { - parameterBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(attribute)); - } - } - } - - var returnParamter = targetMethod.ReturnParameter; - var returnParameterBuilder = methodBuilder.DefineParameter(0, returnParamter.Attributes, returnParamter.Name); - returnParameterBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - foreach (var attribute in returnParamter.CustomAttributes) - { - returnParameterBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(attribute)); - } - } - - // Code from https://github.com/castleproject/Core/blob/master/src/Castle.Core/DynamicProxy/Generators/Emitters/MethodEmitter.cs - private static void CopyDefaultValueConstant(ParameterInfo from, ParameterBuilder to) - { - object defaultValue; - try - { - defaultValue = from.DefaultValue; - } - catch (FormatException) when (from.ParameterType == typeof(DateTime)) - { - // This catch clause guards against a CLR bug that makes it impossible to query - // the default value of an optional DateTime parameter. For the CoreCLR, see - // https://github.com/dotnet/corefx/issues/26164. - - // If this bug is present, it is caused by a `null` default value: - defaultValue = null; - } - catch (FormatException) when (from.ParameterType.IsEnum) - { - // This catch clause guards against a CLR bug that makes it impossible to query - // the default value of a (closed generic) enum parameter. For the CoreCLR, see - // https://github.com/dotnet/corefx/issues/29570. - - // If this bug is present, it is caused by a `null` default value: - defaultValue = null; - } - - if (defaultValue is Missing) - { - // It is likely that we are reflecting over invalid metadata if we end up here. - // At this point, `to.Attributes` will have the `HasDefault` flag set. If we do - // not call `to.SetConstant`, that flag will be reset when creating the dynamic - // type, so `to` will at least end up having valid metadata. It is quite likely - // that the `Missing.Value` will still be reproduced because the `Parameter- - // Builder`'s `ParameterAttributes.Optional` is likely set. (If it isn't set, - // we'll be causing a default value of `DBNull.Value`, but there's nothing that - // can be done about that, short of recreating a new `ParameterBuilder`.) - return; - } - - try - { - to.SetConstant(defaultValue); - } - catch (ArgumentException) - { - var parameterType = from.ParameterType; - var parameterNonNullableType = parameterType; - var isNullableType = parameterType.IsNullableType(); - - if (defaultValue == null) - { - if (isNullableType) - { - // This guards against a Mono bug that prohibits setting default value `null` - // for a `Nullable` parameter. See https://github.com/mono/mono/issues/8504. - // - // If this bug is present, luckily we still get `null` as the default value if - // we do nothing more (which is probably itself yet another bug, as the CLR - // would "produce" a default value of `Missing.Value` in this situation). - return; - } - else if (parameterType.IsValueType) - { - // This guards against a CLR bug that prohibits replicating `null` default - // values for non-nullable value types (which, despite the apparent type - // mismatch, is perfectly legal and something that the Roslyn compilers do). - // For the CoreCLR, see https://github.com/dotnet/corefx/issues/26184. - - // If this bug is present, the best we can do is to not set the default value. - // This will cause a default value of `Missing.Value` (if `ParameterAttributes- - // .Optional` is set) or `DBNull.Value` (otherwise, unlikely). - return; - } - } - else if (isNullableType) - { - parameterNonNullableType = from.ParameterType.GetGenericArguments()[0]; - if (parameterNonNullableType.IsEnum || parameterNonNullableType.IsInstanceOfType(defaultValue)) - { - // This guards against two bugs: - // - // * On the CLR and CoreCLR, a bug that makes it impossible to use `ParameterBuilder- - // .SetConstant` on parameters of a nullable enum type. For CoreCLR, see - // https://github.com/dotnet/coreclr/issues/17893. - // - // If this bug is present, there is no way to faithfully reproduce the default - // value. This will most likely cause a default value of `Missing.Value` or - // `DBNull.Value`. (To better understand which of these, see comment above). - // - // * On Mono, a bug that performs a too-strict type check for nullable types. The - // value passed to `ParameterBuilder.SetConstant` must have a type matching that - // of the parameter precisely. See https://github.com/mono/mono/issues/8597. - // - // If this bug is present, there's no way to reproduce the default value because - // we cannot actually create a value of type `Nullable<>`. - return; - } - } - - // Finally, we might have got here because the metadata constant simply doesn't match - // the parameter type exactly. Some code generators other than the .NET compilers - // might produce such metadata. Make a final attempt to coerce it to the required type: - try - { - var coercedDefaultValue = Convert.ChangeType(defaultValue, parameterNonNullableType, CultureInfo.InvariantCulture); - to.SetConstant(coercedDefaultValue); - - return; - } - catch - { - // We don't care about the error thrown by an unsuccessful type coercion. - } - - throw; - } - } - - internal static void DefineParameters(ConstructorInfo constructor, ConstructorBuilder constructorBuilder) - { - constructorBuilder.DefineParameter(1, ParameterAttributes.None, "aspectContextFactory"); - var parameters = constructor.GetParameters(); - if (parameters.Length > 0) - { - var paramOffset = 2; //ParameterTypes.Length - parameters.Length + 1 - for (var i = 0; i < parameters.Length; i++) - { - var parameter = parameters[i]; - var parameterBuilder = constructorBuilder.DefineParameter(i + paramOffset, parameter.Attributes, parameter.Name); - if (parameter.HasDefaultValue) - { - parameterBuilder.SetConstant(parameter.DefaultValue); - } - parameterBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(typeof(DynamicallyAttribute))); - foreach (var attribute in parameter.CustomAttributes) - { - parameterBuilder.SetCustomAttribute(CustomAttributeBuilderUtils.DefineCustomAttribute(attribute)); - } - } - } - } - } - - private class GenericParameterUtils - { - internal static void DefineGenericParameter(Type targetType, TypeBuilder typeBuilder) - { - if (!targetType.GetTypeInfo().IsGenericTypeDefinition) - { - return; - } - var genericArguments = targetType.GetTypeInfo().GetGenericArguments().Select(t => t.GetTypeInfo()).ToArray(); - var genericArgumentsBuilders = typeBuilder.DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray()); - for (var index = 0; index < genericArguments.Length; index++) - { - genericArgumentsBuilders[index].SetGenericParameterAttributes(ToClassGenericParameterAttributes(genericArguments[index].GenericParameterAttributes)); - foreach (var constraint in genericArguments[index].GetGenericParameterConstraints().Select(t => t.GetTypeInfo())) - { - if (constraint.IsClass) genericArgumentsBuilders[index].SetBaseTypeConstraint(constraint.AsType()); - if (constraint.IsInterface) genericArgumentsBuilders[index].SetInterfaceConstraints(constraint.AsType()); - } - } - } - - internal static void DefineGenericParameter(MethodInfo tergetMethod, MethodBuilder methodBuilder) - { - if (!tergetMethod.IsGenericMethod) - { - return; - } - var genericArguments = tergetMethod.GetGenericArguments().Select(t => t.GetTypeInfo()).ToArray(); - var genericArgumentsBuilders = methodBuilder.DefineGenericParameters(genericArguments.Select(a => a.Name).ToArray()); - for (var index = 0; index < genericArguments.Length; index++) - { - genericArgumentsBuilders[index].SetGenericParameterAttributes(genericArguments[index].GenericParameterAttributes); - foreach (var constraint in genericArguments[index].GetGenericParameterConstraints().Select(t => t.GetTypeInfo())) - { - if (constraint.IsClass) genericArgumentsBuilders[index].SetBaseTypeConstraint(constraint.AsType()); - if (constraint.IsInterface) genericArgumentsBuilders[index].SetInterfaceConstraints(constraint.AsType()); - } - } - } - - private static GenericParameterAttributes ToClassGenericParameterAttributes(GenericParameterAttributes attributes) - { - if (attributes == GenericParameterAttributes.None) - { - return GenericParameterAttributes.None; - } - if (attributes.HasFlag(GenericParameterAttributes.SpecialConstraintMask)) - { - return GenericParameterAttributes.SpecialConstraintMask; - } - if (attributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint)) - { - return GenericParameterAttributes.NotNullableValueTypeConstraint; - } - if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint) && attributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)) - { - return GenericParameterAttributes.ReferenceTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint; - } - if (attributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)) - { - return GenericParameterAttributes.ReferenceTypeConstraint; - } - if (attributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)) - { - return GenericParameterAttributes.DefaultConstructorConstraint; - } - return GenericParameterAttributes.None; - } - } - - private class CustomAttributeBuilderUtils - { - public static CustomAttributeBuilder DefineCustomAttribute(Type attributeType) - { - return new CustomAttributeBuilder(attributeType.GetTypeInfo().GetConstructor(Type.EmptyTypes), ArrayUtils.Empty()); - } - - public static CustomAttributeBuilder DefineCustomAttribute(CustomAttributeData customAttributeData) - { - if (customAttributeData.NamedArguments != null) - { - var attributeTypeInfo = customAttributeData.AttributeType.GetTypeInfo(); - var constructor = customAttributeData.Constructor; - //var constructorArgs = customAttributeData.ConstructorArguments.Select(c => c.Value).ToArray(); - var constructorArgs = customAttributeData.ConstructorArguments - .Select(ReadAttributeValue) - .ToArray(); - var namedProperties = customAttributeData.NamedArguments - .Where(n => !n.IsField) - .Select(n => attributeTypeInfo.GetProperty(n.MemberName)) - .ToArray(); - var propertyValues = customAttributeData.NamedArguments - .Where(n => !n.IsField) - .Select(n => ReadAttributeValue(n.TypedValue)) - .ToArray(); - var namedFields = customAttributeData.NamedArguments.Where(n => n.IsField) - .Select(n => attributeTypeInfo.GetField(n.MemberName)) - .ToArray(); - var fieldValues = customAttributeData.NamedArguments.Where(n => n.IsField) - .Select(n => ReadAttributeValue(n.TypedValue)) - .ToArray(); - return new CustomAttributeBuilder(customAttributeData.Constructor, constructorArgs - , namedProperties - , propertyValues, namedFields, fieldValues); - } - else - { - return new CustomAttributeBuilder(customAttributeData.Constructor, - customAttributeData.ConstructorArguments.Select(c => c.Value).ToArray()); - } - } - - private static object ReadAttributeValue(CustomAttributeTypedArgument argument) - { - var value = argument.Value; - if (argument.ArgumentType.GetTypeInfo().IsArray == false) - { - return value; - } - //special case for handling arrays in attributes - //the actual type of "value" is ReadOnlyCollection. - var arguments = ((IEnumerable)value) - .Select(m => m.Value) - .ToArray(); - return arguments; - } - } - - private class FieldBuilderUtils - { - public const string ActivatorFactory = "_activatorFactory"; - public const string Target = "_implementation"; - - public static FieldTable DefineFields(Type targetType, TypeBuilder typeBuilder) - { - var fieldTable = new FieldTable(); - fieldTable[ActivatorFactory] = typeBuilder.DefineField(ActivatorFactory, typeof(IAspectActivatorFactory), FieldAttributes.Private); - fieldTable[Target] = typeBuilder.DefineField(Target, targetType, FieldAttributes.Private); - return fieldTable; - } - } - - private class FieldTable - { - private readonly Dictionary _table = new Dictionary(); - - public FieldBuilder this[string fieldName] - { - get - { - return _table[fieldName]; - } - set - { - _table[value.Name] = value; - } - } - } - - private class MethodConstantTable - { - private readonly TypeBuilder _nestedTypeBuilder; - private readonly ConstructorBuilder _constructorBuilder; - private readonly ILGenerator _ilGen; - private readonly Dictionary _fields; - - public MethodConstantTable(TypeBuilder typeBuilder) - { - _fields = new Dictionary(); - _nestedTypeBuilder = typeBuilder.DefineNestedType("MethodConstant", TypeAttributes.NestedPrivate); - _constructorBuilder = _nestedTypeBuilder.DefineTypeInitializer(); - _ilGen = _constructorBuilder.GetILGenerator(); - } - - public void AddMethod(string name, MethodInfo method) - { - if (!_fields.ContainsKey(name)) - { - var field = _nestedTypeBuilder.DefineField(name, typeof(MethodInfo), FieldAttributes.Static | FieldAttributes.InitOnly | FieldAttributes.Assembly); - _fields.Add(name, field); - if (method != null) - { - _ilGen.EmitMethod(method); - _ilGen.Emit(OpCodes.Stsfld, field); - } - } - } - - public void LoadMethod(ILGenerator ilGen, string name) - { - if (_fields.TryGetValue(name, out FieldBuilder field)) - { - ilGen.Emit(OpCodes.Ldsfld, field); - return; - } - throw new InvalidOperationException($"Failed to find the method associated with the specified key {name}."); - } - - public void Compile() - { - _ilGen.Emit(OpCodes.Ret); - _nestedTypeBuilder.CreateTypeInfo(); - } - } - - private class TypeDesc - { - public TypeBuilder Builder { get; } - - public FieldTable Fields { get; } - - public MethodConstantTable MethodConstants { get; } - - public Dictionary Properties { get; } - - public Type ServiceType { get; } - - public TypeDesc(Type serviceType, TypeBuilder typeBuilder, FieldTable fields, MethodConstantTable methodConstants) - { - ServiceType = serviceType; - Builder = typeBuilder; - Fields = fields; - MethodConstants = methodConstants; - Properties = new Dictionary(); - } - - public Type Compile() - { - MethodConstants.Compile(); - return Builder.CreateTypeInfo().AsType(); - } - - public T GetProperty() - { - return (T)Properties[typeof(T).Name]; - } - } - } -} \ No newline at end of file diff --git a/tests/AspectCore.Tests/DynamicProxy/AsyncExceptionInterfaceProxyTests.cs b/tests/AspectCore.Tests/DynamicProxy/AsyncExceptionInterfaceProxyTests.cs new file mode 100644 index 00000000..7d7a8fa0 --- /dev/null +++ b/tests/AspectCore.Tests/DynamicProxy/AsyncExceptionInterfaceProxyTests.cs @@ -0,0 +1,93 @@ +using System; +using System.Threading.Tasks; +using AspectCore.Configuration; +using AspectCore.DynamicProxy; +using Xunit; + +namespace AspectCore.Tests.DynamicProxy +{ + public class AsyncExceptionInterfaceProxyTests : DynamicProxyTestBase + { + [Fact] + public async Task InterfaceProxy_Propagates_Async_Exceptions_With_AspectWrapping() + { + var implementation = new AsyncThrowingService(); + var proxy = ProxyGenerator.CreateInterfaceProxy(); + + await AssertWrappedAsyncException( + () => implementation.ThrowAsyncOfTaskResult(), + () => proxy.ThrowAsyncOfTaskResult()); + + await AssertWrappedAsyncException( + () => implementation.ThrowAsyncOfValueTask().AsTask(), + () => proxy.ThrowAsyncOfValueTask().AsTask()); + + await AssertWrappedAsyncException( + () => implementation.ThrowInMainTask(), + () => proxy.ThrowInMainTask()); + } + + private static async Task AssertWrappedAsyncException(Func action, Func proxyAction) + { + try + { + await action(); + } + catch (Exception ex) + { + try + { + await proxyAction(); + } + catch (Exception proxyException) + { + Assert.IsType(proxyException); + Assert.NotNull(proxyException.InnerException); + Assert.Equal(ex.GetType(), proxyException.InnerException.GetType()); + return; + } + + throw new InvalidOperationException("no exception in proxyAction"); + } + + throw new InvalidOperationException("no exception in action"); + } + + public interface IAsyncThrowingService + { + [Nothing] + Task ThrowAsyncOfTaskResult(); + + [Nothing] + ValueTask ThrowAsyncOfValueTask(); + + [Nothing] + Task ThrowInMainTask(); + } + + public class AsyncThrowingService : IAsyncThrowingService + { + public Task ThrowAsyncOfTaskResult() => throw new ArgumentException(); + + public ValueTask ThrowAsyncOfValueTask() => throw new ArgumentException(); + + public Task ThrowInMainTask() + { + return Task.Run(() => throw new ArgumentException()) + .ContinueWith(task => + { + if (task.IsFaulted) + { + throw task.Exception.InnerException; + } + }); + } + } + + protected override void Configure(IAspectConfiguration configuration) + { + configuration.ThrowAspectException = true; + base.Configure(configuration); + } + } +} diff --git a/tests/AspectCore.Tests/DynamicProxy/OpenGenericMethodTests.cs b/tests/AspectCore.Tests/DynamicProxy/OpenGenericMethodTests.cs index 9386d499..c33b81a0 100644 --- a/tests/AspectCore.Tests/DynamicProxy/OpenGenericMethodTests.cs +++ b/tests/AspectCore.Tests/DynamicProxy/OpenGenericMethodTests.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; -using AspectCore.Configuration; +using System; +using System.Reflection; using AspectCore.DynamicProxy; -using Microsoft.Extensions.ObjectPool; using Xunit; namespace AspectCore.Tests.DynamicProxy @@ -11,16 +8,69 @@ namespace AspectCore.Tests.DynamicProxy public class OpenGenericMethodTests : DynamicProxyTestBase { [Fact] - public void OpenGenericProxy() + public void OpenGenericInterfaceProxyType_Without_ImplType_Supports_Generic_Method_Invocation() { - var proxy = ProxyGenerator.CreateClassProxy(); + var proxyType = ProxyGenerator.TypeGenerator.CreateInterfaceProxyType(typeof(IGenericMethodService<>)); + var closedProxyType = proxyType.MakeGenericType(typeof(int)); + var proxy = (IGenericMethodService)Activator.CreateInstance(closedProxyType, GetAspectActivatorFactory()); + proxy.State = 7; + + Assert.Equal(7, proxy.State); + Assert.Null(proxy.Transform(7, "fallback")); + + var transformMethod = closedProxyType.GetMethod(nameof(IGenericMethodService.Transform)); + Assert.NotNull(transformMethod); + Assert.True(transformMethod.IsGenericMethodDefinition); + Assert.Single(transformMethod.GetGenericArguments()); } + [Fact] + public void ClosedClassProxy_Supports_Generic_Method_Invocation() + { + var proxy = ProxyGenerator.CreateClassProxy>(); + + proxy.State = 11; - protected override void Configure(IAspectConfiguration configuration) + Assert.Equal(11, proxy.State); + Assert.Equal("11:fallback", proxy.Transform(11, "fallback")); + + var transformMethod = proxy.GetType().GetMethod(nameof(GenericMethodService.Transform)); + Assert.NotNull(transformMethod); + Assert.True(transformMethod.IsGenericMethodDefinition); + Assert.Single(transformMethod.GetGenericArguments()); + } + + private IAspectActivatorFactory GetAspectActivatorFactory() { - configuration.Interceptors.AddDelegate((ctx, next) => next(ctx)); + var proxyGenerator = ProxyGenerator; + var innerGeneratorField = proxyGenerator.GetType().GetField("_proxyGenerator", BindingFlags.Instance | BindingFlags.NonPublic); + if (innerGeneratorField?.GetValue(proxyGenerator) is IProxyGenerator innerGenerator) + { + proxyGenerator = innerGenerator; + } + + var aspectActivatorFactoryField = proxyGenerator.GetType().GetField("_aspectActivatorFactory", BindingFlags.Instance | BindingFlags.NonPublic); + return (IAspectActivatorFactory)aspectActivatorFactoryField.GetValue(proxyGenerator); + } + + public interface IGenericMethodService + { + TReturn Transform(TState state, TReturn fallback) + where TReturn : class; + + TState State { get; set; } + } + + public class GenericMethodService + { + public virtual TReturn Transform(TState state, TReturn fallback) + where TReturn : class + { + return $"{state}:{fallback}" as TReturn; + } + + public virtual TState State { get; set; } } } -} \ No newline at end of file +} diff --git a/tests/AspectCore.Tests/DynamicProxy/ProxyMetadataParityTests.cs b/tests/AspectCore.Tests/DynamicProxy/ProxyMetadataParityTests.cs new file mode 100644 index 00000000..3a2adb43 --- /dev/null +++ b/tests/AspectCore.Tests/DynamicProxy/ProxyMetadataParityTests.cs @@ -0,0 +1,149 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using AspectCore.Configuration; +using AspectCore.DynamicProxy; +using Xunit; + +namespace AspectCore.Tests.DynamicProxy +{ + public class ProxyMetadataParityTests : DynamicProxyTestBase + { + [Fact] + public void InterfaceProxy_Preserves_Behavior_And_Metadata() + { + var proxy = ProxyGenerator.CreateInterfaceProxy(); + + proxy.Name = "interface"; + + Assert.Equal("interface", proxy.Name); + Assert.Equal("1:payload", proxy.Combine(1, "payload")); + + AssertProxyMetadata( + typeof(IParityService).GetProperty(nameof(IParityService.Name)), + proxy.GetType().GetProperty(nameof(IParityService.Name))); + AssertProxyMetadata( + typeof(IParityService).GetMethod(nameof(IParityService.Combine)), + proxy.GetType().GetMethod(nameof(IParityService.Combine))); + } + + [Fact] + public void ClassProxy_Preserves_Behavior_And_Metadata() + { + var proxy = ProxyGenerator.CreateClassProxy(); + + proxy.Name = "class"; + + Assert.Equal("class", proxy.Name); + Assert.Equal("1:payload", proxy.Combine(1, "payload")); + + AssertProxyMetadata( + typeof(ParityClassService).GetProperty(nameof(ParityClassService.Name)), + proxy.GetType().GetProperty(nameof(ParityClassService.Name))); + AssertProxyMetadata( + typeof(ParityClassService).GetMethod(nameof(ParityClassService.Combine)), + proxy.GetType().GetMethod(nameof(ParityClassService.Combine))); + } + + private static void AssertProxyMetadata(PropertyInfo sourceProperty, PropertyInfo proxyProperty) + { + Assert.NotNull(sourceProperty); + Assert.NotNull(proxyProperty); + + Assert.Equal( + sourceProperty.GetCustomAttribute()?.Description, + proxyProperty.GetCustomAttribute()?.Description); + Assert.NotNull(proxyProperty.GetCustomAttribute()); + } + + private static void AssertProxyMetadata(MethodInfo sourceMethod, MethodInfo proxyMethod) + { + Assert.NotNull(sourceMethod); + Assert.NotNull(proxyMethod); + + Assert.Equal( + sourceMethod.GetCustomAttribute()?.Description, + proxyMethod.GetCustomAttribute()?.Description); + Assert.NotNull(proxyMethod.GetCustomAttribute()); + + var sourceIdParameter = sourceMethod.GetParameters().Single(parameter => parameter.Name == "id"); + var proxyIdParameter = proxyMethod.GetParameters().Single(parameter => parameter.Name == "id"); + + Assert.Equal( + sourceIdParameter.GetCustomAttribute()?.Description, + proxyIdParameter.GetCustomAttribute()?.Description); + Assert.NotNull(proxyIdParameter.GetCustomAttribute()); + + var sourcePayloadParameter = sourceMethod.GetParameters().Single(parameter => parameter.Name == "payload"); + var proxyPayloadParameter = proxyMethod.GetParameters().Single(parameter => parameter.Name == "payload"); + + Assert.Equal( + sourcePayloadParameter.GetCustomAttribute()?.Description, + proxyPayloadParameter.GetCustomAttribute()?.Description); + Assert.NotNull(proxyPayloadParameter.GetCustomAttribute()); + + Assert.Equal( + sourceMethod.ReturnParameter.GetCustomAttribute()?.Description, + proxyMethod.ReturnParameter.GetCustomAttribute()?.Description); + Assert.NotNull(proxyMethod.ReturnParameter.GetCustomAttribute()); + + var sourceGenericArgument = sourceMethod.GetGenericArguments().Single(); + var proxyGenericArgument = proxyMethod.GetGenericArguments().Single(); + + Assert.Equal( + sourceGenericArgument.GetCustomAttribute()?.Description, + proxyGenericArgument.GetCustomAttribute()?.Description); + } + + public interface IParityService + { + [Description(nameof(Name))] + string Name { get; set; } + + [Description(nameof(Combine))] + [return: Description(nameof(CombineReturn))] + string Combine<[Description(nameof(CombineGenericArgument))] TPayload>( + [Description(nameof(CombineId))] int id, + [Description(nameof(CombinePayload))] TPayload payload) + where TPayload : class; + } + + public class ParityInterfaceService : IParityService + { + public string Name { get; set; } + + public string Combine(int id, TPayload payload) + where TPayload : class + { + return $"{id}:{payload}"; + } + } + + public class ParityClassService + { + [Description(nameof(Name))] + public virtual string Name { get; set; } + + [Description(nameof(Combine))] + [return: Description(nameof(CombineReturn))] + public virtual string Combine<[Description(nameof(CombineGenericArgument))] TPayload>( + [Description(nameof(CombineId))] int id, + [Description(nameof(CombinePayload))] TPayload payload) + where TPayload : class + { + return $"{id}:{payload}"; + } + } + + protected override void Configure(IAspectConfiguration configuration) + { + configuration.Interceptors.AddDelegate((ctx, next) => next(ctx)); + } + + private const string CombineReturn = nameof(CombineReturn); + private const string CombineGenericArgument = nameof(CombineGenericArgument); + private const string CombineId = nameof(CombineId); + private const string CombinePayload = nameof(CombinePayload); + } +} From fb522428aa0f18217b53ed5fa708260535118f03 Mon Sep 17 00:00:00 2001 From: "liuhaoyang.qz" Date: Sun, 12 Apr 2026 16:55:17 +0800 Subject: [PATCH 2/2] chore: add .omc to gitignore and remove from tracking --- .gitignore | 3 ++ .omc/graphs/emit-ast-production-hardening.md | 37 -------------------- 2 files changed, 3 insertions(+), 37 deletions(-) delete mode 100644 .omc/graphs/emit-ast-production-hardening.md diff --git a/.gitignore b/.gitignore index 5c337d69..c207240c 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,9 @@ bld/ # Rider config directory .idea/ +# OMC orchestrator state +.omc/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* diff --git a/.omc/graphs/emit-ast-production-hardening.md b/.omc/graphs/emit-ast-production-hardening.md deleted file mode 100644 index 47b98c28..00000000 --- a/.omc/graphs/emit-ast-production-hardening.md +++ /dev/null @@ -1,37 +0,0 @@ -# Graph: emit-ast-production-hardening - -> Status: completed | Iteration: 3/6 - -## User Intent -- Original: 看一下当前做的 emit AST 的重构. 帮我继续优化到生产可用,以及完整的验证 -- Acceptance: 盘清当前 emit AST 重构状态与风险点;补齐达到生产可用所需的实现优化;完成独立、完整、可复述的验证,并报告剩余风险。 - -## Understanding -- 用户要在当前仓库中继续推进 emit AST 重构,不是从零开始。 -- 成功标准强调“生产可用”和“完整验证”,需要实现与独立验证分离。 -- 当前 emit AST 已接管 DynamicProxy 主生成路径,边界是“代理类型/成员/元数据 AST 化 + ILEmitVisitor 输出 IL”,不是方法体逐指令 AST 化。 -- 当前主要缺口不是主流程缺失,而是生产证据与硬化不足:interface proxy parity、open generic/generic method 行为覆盖、异步异常边角、proxy emit benchmark、netstandard/跨平台兼容性证据。 - -## Completed -### n1 @omc-explore — DONE_WITH_CONCERNS -- Summary: emit AST 重构主链路已落地,但还缺生产化验证闭环。 -- Findings: 主流程由 ProxyTypeCompiler + *AstBuilder + Nodes + ILEmitVisitor 组成;无旧新实现并存切换;测试偏 class-heavy,interface/generic/异常/性能/兼容性证据不足。 -- Deliverables: 核心代码地图、验证入口、最小后续执行清单。 - -### n2 @omc-deep-executor — DONE_WITH_CONCERNS -- Summary: 补齐 interface/generic/异常/性能/兼容性关键缺口,新增 benchmark,并做代表性本地验证。 -- Findings: 修复返回参数位置与 generic parameter attribute 回放;补 interface-only open generic stub 的 generic method 定义;收紧异步返回值 await 语义;新增 proxy emit benchmark。 -- Deliverables: 新增/修改 src、tests、benchmark 多处文件;本地验证通过(DynamicProxy 过滤测试、netstandard2.0/2.1 build、ProxyEmitBenchmarks 运行)。 - -### n3 @omc-verifier — DONE_WITH_CONCERNS -- Summary: 独立验证通过,本轮目标可认为完成,且显著接近生产可用。 -- Findings: DynamicProxy 子集、核心测试工程全框架、整仓 solution build/test、netstandard build、ProxyEmitBenchmarks 均通过;未发现新的功能性回归。 -- Deliverables: 独立验收结论为 PASS;明确两个残留边界——`ThrowInUncontinuedTasks` 未重验、`AspectCore.Core.csproj` 存在 `.net9.0` 单框架构建瑕疵。 - -## In Flight - -## Pending - - -## Budget -- Iterations: 3 / 6