forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathProxyBuilderHelper.cs
258 lines (214 loc) · 8.89 KB
/
ProxyBuilderHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#region Credits
// Part of this work is based on LinFu.DynamicProxy framework (c) Philip Laureano who has donated it to NHibernate project.
// The license is the same of NHibernate license (LGPL Version 2.1, February 1999).
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Security;
using NHibernate.Util;
namespace NHibernate.Proxy
{
internal static class ProxyBuilderHelper
{
private static readonly ConstructorInfo ObjectConstructor = typeof(object).GetConstructor(System.Type.EmptyTypes);
private static readonly ConstructorInfo SecurityCriticalAttributeConstructor = typeof(SecurityCriticalAttribute).GetConstructor(System.Type.EmptyTypes);
private static readonly ConstructorInfo IgnoresAccessChecksToAttributeConstructor = typeof(IgnoresAccessChecksToAttribute).GetConstructor(new[] {typeof(string)});
internal static readonly MethodInfo SerializableGetObjectDataMethod = typeof(ISerializable).GetMethod(nameof(ISerializable.GetObjectData));
internal static readonly MethodInfo SerializationInfoSetTypeMethod = ReflectHelper.GetMethod<SerializationInfo>(si => si.SetType(null));
#if NETFX
private static bool _saveAssembly;
private static string _saveAssemblyPath;
// Called by reflection
internal static void EnableDynamicAssemblySaving(bool enable, string saveAssemblyPath)
{
_saveAssembly = enable;
_saveAssemblyPath = saveAssemblyPath;
}
#endif
internal static AssemblyBuilder DefineDynamicAssembly(AppDomain appDomain, AssemblyName name)
{
#if NETFX
var access = _saveAssembly ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run;
var assemblyBuilder = _saveAssembly && !string.IsNullOrEmpty(_saveAssemblyPath)
? appDomain.DefineDynamicAssembly(name, access, Path.GetDirectoryName(_saveAssemblyPath))
: AssemblyBuilder.DefineDynamicAssembly(name, access);
#else
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
#endif
return assemblyBuilder;
}
internal static ModuleBuilder DefineDynamicModule(AssemblyBuilder assemblyBuilder, string moduleName)
{
#if NETFX
var moduleBuilder = _saveAssembly
? assemblyBuilder.DefineDynamicModule(moduleName, $"{moduleName}.mod", true)
: assemblyBuilder.DefineDynamicModule(moduleName);
#else
var moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
#endif
return moduleBuilder;
}
internal static void Save(AssemblyBuilder assemblyBuilder)
{
#if NETFX
if (_saveAssembly)
assemblyBuilder.Save(
string.IsNullOrEmpty(_saveAssemblyPath)
? "generatedAssembly.dll"
: Path.GetFileName(_saveAssemblyPath));
#endif
}
internal static void CallDefaultBaseConstructor(ILGenerator il, System.Type parentType)
{
var baseConstructor = parentType.GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
null,
System.Type.EmptyTypes,
null);
// if there is no default constructor, or the default constructor is private/internal, call System.Object constructor
// this works, but the generated assembly will fail PeVerify (cannot use in medium trust for example)
if (baseConstructor == null || baseConstructor.IsPrivate || baseConstructor.IsAssembly)
baseConstructor = ObjectConstructor;
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, baseConstructor);
}
internal static IEnumerable<MethodInfo> GetProxiableMethods(System.Type type)
{
const BindingFlags candidateMethodsBindingFlags =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
return type.GetMethods(candidateMethodsBindingFlags).Where(m => m.IsProxiable());
}
internal static IEnumerable<MethodInfo> GetProxiableMethods(System.Type type, IEnumerable<System.Type> interfaces)
{
var proxiableMethods =
GetProxiableMethods(type)
.Concat(interfaces.SelectMany(i => i.GetMethods()))
.Distinct();
return proxiableMethods;
}
internal static void MakeProxySerializable(TypeBuilder typeBuilder)
{
var serializableConstructor = typeof(SerializableAttribute).GetConstructor(System.Type.EmptyTypes);
var customAttributeBuilder = new CustomAttributeBuilder(serializableConstructor, Array.Empty<object>());
typeBuilder.SetCustomAttribute(customAttributeBuilder);
}
internal static MethodBuilder GetObjectDataMethodBuilder(TypeBuilder typeBuilder)
{
const MethodAttributes attributes =
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual;
var parameterTypes = new[] { typeof(SerializationInfo), typeof(StreamingContext) };
var methodBuilder = typeBuilder.DefineMethod(
nameof(ISerializable.GetObjectData),
attributes,
typeof(void),
parameterTypes);
methodBuilder.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.Managed);
methodBuilder.SetCustomAttribute(
new CustomAttributeBuilder(SecurityCriticalAttributeConstructor, Array.Empty<object>()));
return methodBuilder;
}
internal static MethodBuilder GenerateMethodSignature(string name, MethodInfo method, TypeBuilder typeBuilder)
{
//TODO: Should we use attributes of base method?
var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual;
if (method.IsSpecialName)
methodAttributes |= MethodAttributes.SpecialName;
var parameters = method.GetParameters();
var methodBuilder = typeBuilder.DefineMethod(
name,
methodAttributes,
CallingConventions.HasThis,
method.ReturnType,
parameters.Select(param => param.ParameterType).ToArray());
var typeArgs = method.GetGenericArguments();
if (typeArgs.Length > 0)
{
var typeNames = GenerateTypeNames(typeArgs.Length);
var typeArgBuilders = methodBuilder.DefineGenericParameters(typeNames);
for (var index = 0; index < typeArgs.Length; index++)
{
// Copy generic parameter attributes (Covariant, Contravariant, ReferenceTypeConstraint,
// NotNullableValueTypeConstraint, DefaultConstructorConstraint).
var typeArgBuilder = typeArgBuilders[index];
var typeArg = typeArgs[index];
typeArgBuilder.SetGenericParameterAttributes(typeArg.GenericParameterAttributes);
// Copy generic parameter constraints (class and interfaces).
var typeConstraints = typeArg.GetGenericParameterConstraints()
.Select(x => ResolveTypeConstraint(method, x))
.ToArray();
var baseTypeConstraint = typeConstraints.SingleOrDefault(x => x.IsClass);
typeArgBuilder.SetBaseTypeConstraint(baseTypeConstraint);
var interfaceTypeConstraints = typeConstraints.Where(x => !x.IsClass).ToArray();
typeArgBuilder.SetInterfaceConstraints(interfaceTypeConstraints);
}
}
return methodBuilder;
}
internal static void GenerateInstanceOfIgnoresAccessChecksToAttribute(
AssemblyBuilder assemblyBuilder,
string assemblyName)
{
// [assembly: System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute(assemblyName)]
var customAttributeBuilder = new CustomAttributeBuilder(
IgnoresAccessChecksToAttributeConstructor,
new object[] {assemblyName});
assemblyBuilder.SetCustomAttribute(customAttributeBuilder);
}
private static System.Type ResolveTypeConstraint(MethodInfo method, System.Type typeConstraint)
{
if (typeConstraint != null && typeConstraint.IsGenericType)
{
var declaringType = method.DeclaringType;
if (declaringType != null && declaringType.IsGenericType)
{
return BuildTypeConstraint(typeConstraint, declaringType);
}
}
return typeConstraint;
}
private static System.Type BuildTypeConstraint(System.Type typeConstraint, System.Type declaringType)
{
var constraintGenericArguments = typeConstraint.GetGenericArguments();
var declaringTypeGenericArguments = declaringType.GetGenericArguments();
var parametersMap = declaringType
.GetGenericTypeDefinition()
.GetGenericArguments()
.ToDictionary(x => x, x => declaringTypeGenericArguments[x.GenericParameterPosition]);
var args = new System.Type[constraintGenericArguments.Length];
var make = false;
for (var index = 0; index < constraintGenericArguments.Length; index++)
{
var genericArgument = constraintGenericArguments[index];
if (parametersMap.TryGetValue(genericArgument, out var result))
{
make = true;
}
else
{
result = genericArgument;
}
args[index] = result;
}
if (make)
{
return typeConstraint.GetGenericTypeDefinition().MakeGenericType(args);
}
return typeConstraint;
}
private static string[] GenerateTypeNames(int count)
{
var result = new string[count];
for (var index = 0; index < count; index++)
{
result[index] = $"T{index}";
}
return result;
}
}
}