Skip to content

Commit feac078

Browse files
committed
Enable PersistentProxyBuilder on .NET 9+ (albeit without type cache)
1 parent dfa3f13 commit feac078

6 files changed

Lines changed: 86 additions & 10 deletions

File tree

src/Castle.Core/DynamicProxy/DefaultProxyBuilder.cs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ namespace Castle.DynamicProxy
2020
using System.Collections.Generic;
2121
using System.Linq;
2222
using System.Reflection;
23+
#if NET9_0_OR_GREATER
24+
using System.IO;
25+
using System.Reflection.Emit;
26+
using System.Runtime.Loader;
27+
#endif
2328

2429
using Castle.Core.Internal;
2530
using Castle.Core.Logging;
@@ -30,9 +35,14 @@ namespace Castle.DynamicProxy
3035
/// </summary>
3136
public class DefaultProxyBuilder : IProxyBuilder
3237
{
33-
private readonly ModuleScope scope;
38+
private ModuleScope scope;
3439
private ILogger logger = NullLogger.Instance;
3540

41+
#if NET9_0_OR_GREATER
42+
protected ModuleScope lastScope;
43+
protected MemoryStream lastAssemblyGenerated;
44+
#endif
45+
3646
/// <summary>
3747
/// Initializes a new instance of the <see cref = "DefaultProxyBuilder" /> class with new <see cref = "ModuleScope" />.
3848
/// </summary>
@@ -68,7 +78,7 @@ public Type CreateClassProxyType(Type classToProxy, Type[]? additionalInterfaces
6878
AssertValidMixins(options, nameof(options));
6979

7080
var generator = new ClassProxyGenerator(scope, classToProxy, additionalInterfacesToProxy, options) { Logger = logger };
71-
return generator.GetProxyType();
81+
return ToInstantiableType(generator.GetProxyType(), generator);
7282
}
7383

7484
public Type CreateClassProxyTypeWithTarget(Type classToProxy, Type[]? additionalInterfacesToProxy,
@@ -80,7 +90,7 @@ public Type CreateClassProxyTypeWithTarget(Type classToProxy, Type[]? additional
8090

8191
var generator = new ClassProxyWithTargetGenerator(scope, classToProxy, additionalInterfacesToProxy, options)
8292
{ Logger = logger };
83-
return generator.GetProxyType();
93+
return ToInstantiableType(generator.GetProxyType(), generator);
8494
}
8595

8696
public Type CreateInterfaceProxyTypeWithTarget(Type interfaceToProxy, Type[]? additionalInterfacesToProxy,
@@ -92,7 +102,7 @@ public Type CreateInterfaceProxyTypeWithTarget(Type interfaceToProxy, Type[]? ad
92102
AssertValidMixins(options, nameof(options));
93103

94104
var generator = new InterfaceProxyWithTargetGenerator(scope, interfaceToProxy, additionalInterfacesToProxy, targetType, options) { Logger = logger };
95-
return generator.GetProxyType();
105+
return ToInstantiableType(generator.GetProxyType(), generator);
96106
}
97107

98108
public Type CreateInterfaceProxyTypeWithTargetInterface(Type interfaceToProxy, Type[]? additionalInterfacesToProxy,
@@ -103,7 +113,7 @@ public Type CreateInterfaceProxyTypeWithTargetInterface(Type interfaceToProxy, T
103113
AssertValidMixins(options, nameof(options));
104114

105115
var generator = new InterfaceProxyWithTargetInterfaceGenerator(scope, interfaceToProxy, additionalInterfacesToProxy, interfaceToProxy, options) { Logger = logger };
106-
return generator.GetProxyType();
116+
return ToInstantiableType(generator.GetProxyType(), generator);
107117
}
108118

109119
public Type CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy, Type[]? additionalInterfacesToProxy,
@@ -114,7 +124,28 @@ public Type CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy, Type[]?
114124
AssertValidMixins(options, nameof(options));
115125

116126
var generator = new InterfaceProxyWithoutTargetGenerator(scope, interfaceToProxy, additionalInterfacesToProxy, typeof(object), options) { Logger = logger };
117-
return generator.GetProxyType();
127+
return ToInstantiableType(generator.GetProxyType(), generator);
128+
}
129+
130+
private Type ToInstantiableType(Type type, BaseProxyGenerator generator)
131+
{
132+
#if NET9_0_OR_GREATER
133+
if (type.Assembly is PersistedAssemblyBuilder persistedAssemblyBuilder)
134+
{
135+
lastAssemblyGenerated?.Dispose();
136+
var stream = new MemoryStream();
137+
persistedAssemblyBuilder.Save(stream);
138+
stream.Seek(0, SeekOrigin.Begin);
139+
var assembly = AssemblyLoadContext.Default.LoadFromStream(stream);
140+
type = assembly.GetType(type.FullName!)!;
141+
lastAssemblyGenerated = stream;
142+
lastScope = scope;
143+
scope = scope.Recycle();
144+
}
145+
146+
generator.InitializeStaticFields(type);
147+
#endif
148+
return type;
118149
}
119150

120151
private void AssertValidMixins(ProxyGenerationOptions options, string paramName)

src/Castle.Core/DynamicProxy/Generators/BaseClassProxyGenerator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ protected sealed override Type GenerateType(string name, INamingScope namingScop
102102
// Crosses fingers and build type
103103

104104
var proxyType = emitter.BuildType();
105+
#if !NET9_0_OR_GREATER
105106
InitializeStaticFields(proxyType);
107+
#endif
106108
return proxyType;
107109
}
108110

src/Castle.Core/DynamicProxy/Generators/BaseInterfaceProxyGenerator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ protected override Type GenerateType(string typeName, INamingScope namingScope)
137137

138138
// Crosses fingers and build type
139139
var generatedType = emitter.BuildType();
140-
140+
#if !NET9_0_OR_GREATER
141141
InitializeStaticFields(generatedType);
142+
#endif
142143
return generatedType;
143144
}
144145

src/Castle.Core/DynamicProxy/Generators/BaseProxyGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ protected void HandleExplicitlyPassedProxyTargetAccessor(ICollection<Type> targe
384384
}
385385
}
386386

387-
protected void InitializeStaticFields(Type builtType)
387+
internal void InitializeStaticFields(Type builtType)
388388
{
389389
builtType.SetStaticField("proxyGenerationOptions", BindingFlags.NonPublic, ProxyGenerationOptions);
390390
}

src/Castle.Core/DynamicProxy/ModuleScope.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,13 @@ internal INamingScope NamingScope
150150

151151
internal SynchronizedDictionary<CacheKey, Type> TypeCache => typeCache;
152152

153+
#if NET9_0_OR_GREATER
154+
internal ModuleScope Recycle()
155+
{
156+
return new ModuleScope(savePhysicalAssembly, disableSignedModule, strongAssemblyName, strongModulePath, weakAssemblyName, weakModulePath);
157+
}
158+
#endif
159+
153160
/// <summary>
154161
/// Gets the key pair used to sign the strong-named assembly generated by this <see cref = "ModuleScope" />.
155162
/// </summary>
@@ -336,6 +343,14 @@ private ModuleBuilder CreateModule(bool signStrongName)
336343
return module;
337344
}
338345
else
346+
#elif NET9_0_OR_GREATER
347+
if (savePhysicalAssembly)
348+
{
349+
AssemblyBuilder assemblyBuilder = new PersistedAssemblyBuilder(assemblyName, typeof(object).Assembly);
350+
var module = assemblyBuilder.DefineDynamicModule(moduleName);
351+
return module;
352+
}
353+
else
339354
#endif
340355
{
341356
#if FEATURE_APPDOMAIN
@@ -368,7 +383,7 @@ private AssemblyName GetAssemblyName(bool signStrongName)
368383
return assemblyName;
369384
}
370385

371-
#if FEATURE_ASSEMBLYBUILDER_SAVE
386+
#if FEATURE_ASSEMBLYBUILDER_SAVE || NET9_0_OR_GREATER
372387
/// <summary>
373388
/// Saves the generated assembly with the name and directory information given when this <see cref = "ModuleScope" /> instance was created (or with
374389
/// the <see cref = "DEFAULT_FILE_NAME" /> and current directory if none was given).
@@ -450,7 +465,11 @@ private AssemblyName GetAssemblyName(bool signStrongName)
450465
}
451466
assemblyBuilder = (AssemblyBuilder)StrongNamedModule.Assembly;
452467
assemblyFileName = StrongNamedModuleName;
468+
#if NET9_0_OR_GREATER
469+
assemblyFilePath = weakModulePath;
470+
#else
453471
assemblyFilePath = StrongNamedModule.FullyQualifiedName;
472+
#endif
454473
}
455474
else
456475
{
@@ -460,7 +479,11 @@ private AssemblyName GetAssemblyName(bool signStrongName)
460479
}
461480
assemblyBuilder = (AssemblyBuilder)WeakNamedModule.Assembly;
462481
assemblyFileName = WeakNamedModuleName;
482+
#if NET9_0_OR_GREATER
483+
assemblyFilePath = weakModulePath;
484+
#else
463485
assemblyFilePath = WeakNamedModule.FullyQualifiedName;
486+
#endif
464487
}
465488

466489
if (File.Exists(assemblyFilePath))
@@ -472,7 +495,12 @@ private AssemblyName GetAssemblyName(bool signStrongName)
472495
AddCacheMappings(assemblyBuilder);
473496
#endif
474497

498+
#if FEATURE_ASSEMBLYBUILDER_SAVE
475499
assemblyBuilder.Save(assemblyFileName);
500+
#elif NET9_0_OR_GREATER
501+
var persistedAssemblyBuilder = (PersistedAssemblyBuilder)assemblyBuilder;
502+
persistedAssemblyBuilder.Save(assemblyFilePath);
503+
#endif
476504
return assemblyFilePath;
477505
}
478506
#endif

src/Castle.Core/DynamicProxy/PersistentProxyBuilder.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
#if FEATURE_ASSEMBLYBUILDER_SAVE
15+
#if FEATURE_ASSEMBLYBUILDER_SAVE || NET9_0_OR_GREATER
1616

1717
#nullable enable
1818

1919
namespace Castle.DynamicProxy
2020
{
21+
#if NET9_0_OR_GREATER
22+
using System.IO;
23+
#endif
24+
2125
/// <summary>
2226
/// ProxyBuilder that persists the generated type.
2327
/// </summary>
@@ -43,7 +47,17 @@ public PersistentProxyBuilder() : base(new ModuleScope(true))
4347
/// </remarks>
4448
public string? SaveAssembly()
4549
{
50+
#if NET9_0_OR_GREATER
51+
var assemblyPath = lastScope.WeakNamedModule != null ? lastScope.WeakNamedModuleName : lastScope.StrongNamedModuleName;
52+
using var file = File.Create(assemblyPath);
53+
lastAssemblyGenerated.Seek(0, SeekOrigin.Begin);
54+
lastAssemblyGenerated.CopyTo(file);
55+
file.Flush();
56+
file.Close();
57+
return assemblyPath;
58+
#else
4659
return ModuleScope.SaveAssembly();
60+
#endif
4761
}
4862
}
4963
}

0 commit comments

Comments
 (0)