forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProxyBuilderHelper.cs
370 lines (312 loc) · 13.2 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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
#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.Proxy.DynamicProxy;
using NHibernate.Util;
namespace NHibernate.Proxy
{
internal static class ProxyBuilderHelper
{
private const BindingFlags ProxiableMethodsBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
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)
{
return type.GetMethods(ProxiableMethodsBindingFlags).Where(m => m.IsProxiable());
}
internal static IEnumerable<MethodInfo> GetProxiableMethods(System.Type type, IEnumerable<System.Type> interfaces)
{
if (type.IsInterface || type == typeof(object) || type.GetInterfaces().Length == 0)
{
return GetProxiableMethods(type)
.Concat(interfaces.SelectMany(i => i.GetMethods(ProxiableMethodsBindingFlags)))
.Distinct();
}
var proxiableMethods = new HashSet<MethodInfo>(GetProxiableMethods(type), new MethodInfoComparer(type));
foreach (var interfaceMethod in interfaces.SelectMany(i => i.GetMethods(ProxiableMethodsBindingFlags)))
{
proxiableMethods.Add(interfaceMethod);
}
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)
{
var explicitImplementation = method.DeclaringType.IsInterface;
if (explicitImplementation &&
(typeBuilder.BaseType == typeof(object) ||
#pragma warning disable 618
typeBuilder.BaseType == typeof(ProxyDummy)) &&
#pragma warning restore 618
(IsEquals(method) || IsGetHashCode(method)))
{
// If we are building a proxy for an interface, and it defines an Equals or GetHashCode, they must
// be implicitly implemented for overriding object methods.
// (Ideally we should check the method actually comes from the interface declared for the proxy.)
explicitImplementation = false;
}
var methodAttributes = explicitImplementation
? MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual
: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual;
if (method.IsSpecialName)
methodAttributes |= MethodAttributes.SpecialName;
var parameters = method.GetParameters();
var implementationName = explicitImplementation
? $"{method.DeclaringType.FullName}.{name}"
: name;
var methodBuilder =
typeBuilder.DefineMethod(
implementationName,
methodAttributes,
CallingConventions.HasThis,
method.ReturnType,
method.ReturnParameter?.GetRequiredCustomModifiers(),
method.ReturnParameter?.GetOptionalCustomModifiers(),
parameters.ToArray(p => p.ParameterType),
parameters.ToArray(p => p.GetRequiredCustomModifiers()),
parameters.ToArray(p => p.GetOptionalCustomModifiers()));
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()
.ToArray(x => ResolveTypeConstraint(method, x));
var baseTypeConstraint = typeConstraints.SingleOrDefault(x => x.IsClass);
typeArgBuilder.SetBaseTypeConstraint(baseTypeConstraint);
var interfaceTypeConstraints = typeConstraints.Where(x => !x.IsClass).ToArray();
typeArgBuilder.SetInterfaceConstraints(interfaceTypeConstraints);
}
}
if (explicitImplementation)
methodBuilder.SetImplementationFlags(MethodImplAttributes.Managed | MethodImplAttributes.IL);
return methodBuilder;
}
private static bool IsGetHashCode(MethodBase method)
{
return method.Name == "GetHashCode" && method.GetParameters().Length == 0;
}
private static bool IsEquals(MethodBase method)
{
if (method.Name != "Equals") return false;
var parameters = method.GetParameters();
return parameters.Length == 1 && parameters[0].ParameterType == typeof(object);
}
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;
}
/// <summary>
/// Method equality for the proxy building purpose: we want to equate an interface method to a base type
/// method which implements it. This implies the base type method has the same signature and there is no
/// explicit implementation of the interface method in the base type.
/// </summary>
private class MethodInfoComparer : IEqualityComparer<MethodInfo>
{
private readonly Dictionary<System.Type, Dictionary<MethodInfo, MethodInfo>> _interfacesMap;
public MethodInfoComparer(System.Type baseType)
{
_interfacesMap = BuildInterfacesMap(baseType);
}
private static Dictionary<System.Type, Dictionary<MethodInfo, MethodInfo>> BuildInterfacesMap(System.Type type)
{
return type.GetInterfaces()
.Distinct()
.ToDictionary(i => i, i => ToDictionary(type.GetInterfaceMap(i)));
}
private static Dictionary<MethodInfo, MethodInfo> ToDictionary(InterfaceMapping interfaceMap)
{
var map = new Dictionary<MethodInfo, MethodInfo>(interfaceMap.InterfaceMethods.Length);
for (var i = 0; i < interfaceMap.InterfaceMethods.Length; i++)
{
map.Add(interfaceMap.InterfaceMethods[i], interfaceMap.TargetMethods[i]);
}
return map;
}
public bool Equals(MethodInfo x, MethodInfo y)
{
if (x == y)
return true;
if (x == null || y == null)
return false;
if (x.Name != y.Name)
return false;
// If they have the same declaring type, one cannot be the implementation of the other.
if (x.DeclaringType == y.DeclaringType)
return false;
// If they belong to two different interfaces or to two different concrete types, one cannot be the
// implementation of the other.
if (x.DeclaringType.IsInterface == y.DeclaringType.IsInterface)
return false;
var interfaceMethod = x.DeclaringType.IsInterface ? x : y;
// If the interface is not implemented by the base type, the method cannot be implemented by the
// base type method.
if (!_interfacesMap.TryGetValue(interfaceMethod.DeclaringType, out var map))
return false;
var baseMethod = x.DeclaringType.IsInterface ? y : x;
return map[interfaceMethod] == baseMethod;
}
public int GetHashCode(MethodInfo obj)
{
// Hashing by name only, putting methods with the same name in the same bucket, in order to keep
// this method fast.
return obj.Name.GetHashCode();
}
}
}
}