Skip to content

Commit d88e944

Browse files
authored
Merge pull request castleproject#580 from stakx/generic-parameter-name-mismatch
Match generic parameters by position, not by name
2 parents eb45161 + c3780f1 commit d88e944

File tree

6 files changed

+92
-169
lines changed

6 files changed

+92
-169
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Enhancements:
88
- Significant performance improvements with proxy type generation for interface proxies without target. (Up until now, DynamicProxy generated a separate `IInvocation` implementation type for every single proxied method – it is now able to reuse a single predefined type in many cases, thereby reducing the total amount of dynamic type generation.) (@stakx, #573)
99

1010
Bugfixes:
11+
- Generic method with differently named generic arguments to parent throws `KeyNotFoundException` (@stakx, #106)
1112
- Proxying certain `[Serializable]` classes produces proxy types that fail PEVerify test (@stakx, #367)
1213
- `private protected` methods are not intercepted (@CrispyDrone, #535)
1314
- `System.UIntPtr` unsupported (@stakx, #546)

src/Castle.Core.Tests/DynamicProxy.Tests/GenericMethodsProxyTestCase.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,32 @@ public void ProxyInterfaceWithGenericMethodWithTwoGenericParametersWhereOneIsBas
9292
{
9393
generator.CreateInterfaceProxyWithoutTarget<GenericMethodWhereOneGenParamInheritsTheOther>();
9494
}
95+
96+
[Test]
97+
[TestCase(typeof(Test))]
98+
[TestCase(typeof(TestVirtual))]
99+
public void GenericMethodDifferentlyNamedGenericArguments(Type classType)
100+
{
101+
generator.CreateClassProxy(classType, new[] { typeof(ITest) });
102+
}
103+
104+
public interface ITest
105+
{
106+
void Hi<T>();
107+
}
108+
109+
public class Test : ITest
110+
{
111+
public void Hi<U>()
112+
{
113+
}
114+
}
115+
116+
public class TestVirtual : ITest
117+
{
118+
public virtual void Hi<U>()
119+
{
120+
}
121+
}
95122
}
96123
}

src/Castle.Core/DynamicProxy/Generators/Emitters/AbstractTypeEmitter.cs

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ internal abstract class AbstractTypeEmitter
3636

3737
private readonly List<MethodEmitter> methods;
3838

39-
private readonly Dictionary<string, GenericTypeParameterBuilder> name2GenericType;
4039
private readonly List<NestedClassEmitter> nested;
4140
private readonly List<PropertyEmitter> properties;
4241
private readonly TypeBuilder typebuilder;
@@ -51,7 +50,6 @@ protected AbstractTypeEmitter(TypeBuilder typeBuilder)
5150
constructors = new List<ConstructorEmitter>();
5251
properties = new List<PropertyEmitter>();
5352
events = new List<EventEmitter>();
54-
name2GenericType = new Dictionary<string, GenericTypeParameterBuilder>();
5553
}
5654

5755
public Type BaseType
@@ -113,7 +111,7 @@ public void CopyGenericParametersFromMethod(MethodInfo methodToCopyGenericsFrom)
113111
throw new InvalidOperationException("Cannot invoke me twice");
114112
}
115113

116-
SetGenericTypeParameters(GenericUtil.CopyGenericArguments(methodToCopyGenericsFrom, typebuilder, name2GenericType));
114+
SetGenericTypeParameters(GenericUtil.CopyGenericArguments(methodToCopyGenericsFrom, typebuilder));
117115
}
118116

119117
public ConstructorEmitter CreateConstructor(params ArgumentReference[] arguments)
@@ -266,42 +264,72 @@ public FieldReference GetField(string name)
266264
return value;
267265
}
268266

269-
public Type GetGenericArgument(string genericArgumentName)
267+
public Type GetClosedParameterType(Type parameter)
270268
{
271-
if (name2GenericType.TryGetValue(genericArgumentName, out var genericTypeParameterBuilder))
272-
return genericTypeParameterBuilder;
269+
if (parameter.IsGenericType)
270+
{
271+
// ECMA-335 section II.9.4: "The CLI does not support partial instantiation
272+
// of generic types. And generic types shall not appear uninstantiated any-
273+
// where in metadata signature blobs." (And parameters are defined there!)
274+
Debug.Assert(parameter.IsGenericTypeDefinition == false);
273275

274-
return null;
275-
}
276+
var arguments = parameter.GetGenericArguments();
277+
if (CloseGenericParametersIfAny(arguments))
278+
{
279+
return parameter.GetGenericTypeDefinition().MakeGenericType(arguments);
280+
}
281+
}
276282

277-
public Type[] GetGenericArgumentsFor(Type genericType)
278-
{
279-
var types = new List<Type>();
283+
if (parameter.IsGenericParameter)
284+
{
285+
return GetGenericArgument(parameter.GenericParameterPosition);
286+
}
280287

281-
foreach (var genType in genericType.GetGenericArguments())
288+
if (parameter.IsArray)
282289
{
283-
if (genType.IsGenericParameter)
284-
{
285-
types.Add(name2GenericType[genType.Name]);
286-
}
287-
else
290+
var elementType = GetClosedParameterType(parameter.GetElementType());
291+
int rank = parameter.GetArrayRank();
292+
return rank == 1
293+
? elementType.MakeArrayType()
294+
: elementType.MakeArrayType(rank);
295+
}
296+
297+
if (parameter.IsByRef)
298+
{
299+
var elementType = GetClosedParameterType(parameter.GetElementType());
300+
return elementType.MakeByRefType();
301+
}
302+
303+
return parameter;
304+
305+
bool CloseGenericParametersIfAny(Type[] arguments)
306+
{
307+
var hasAnyGenericParameters = false;
308+
for (var i = 0; i < arguments.Length; i++)
288309
{
289-
types.Add(genType);
310+
var newType = GetClosedParameterType(arguments[i]);
311+
if (newType != null && !ReferenceEquals(newType, arguments[i]))
312+
{
313+
arguments[i] = newType;
314+
hasAnyGenericParameters = true;
315+
}
290316
}
317+
return hasAnyGenericParameters;
291318
}
319+
}
320+
321+
public Type GetGenericArgument(int position)
322+
{
323+
Debug.Assert(0 <= position && position < genericTypeParams.Length);
292324

293-
return types.ToArray();
325+
return genericTypeParams[position];
294326
}
295327

296328
public Type[] GetGenericArgumentsFor(MethodInfo genericMethod)
297329
{
298-
var types = new List<Type>();
299-
foreach (var genType in genericMethod.GetGenericArguments())
300-
{
301-
types.Add(name2GenericType[genType.Name]);
302-
}
330+
Debug.Assert(genericMethod.GetGenericArguments().Length == genericTypeParams.Length);
303331

304-
return types.ToArray();
332+
return genericTypeParams;
305333
}
306334

307335
public void SetGenericTypeParameters(GenericTypeParameterBuilder[] genericTypeParameterBuilders)

src/Castle.Core/DynamicProxy/Generators/Emitters/GenericUtil.cs

Lines changed: 4 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
namespace Castle.DynamicProxy.Generators.Emitters
1616
{
1717
using System;
18-
using System.Collections.Generic;
1918
using System.Diagnostics;
2019
using System.Reflection;
2120
using System.Reflection.Emit;
@@ -29,95 +28,16 @@ internal class GenericUtil
2928
{
3029
public static GenericTypeParameterBuilder[] CopyGenericArguments(
3130
MethodInfo methodToCopyGenericsFrom,
32-
TypeBuilder builder,
33-
Dictionary<string, GenericTypeParameterBuilder> name2GenericType)
31+
TypeBuilder builder)
3432
{
35-
return
36-
CopyGenericArguments(methodToCopyGenericsFrom, name2GenericType,
37-
builder.DefineGenericParameters);
33+
return CopyGenericArguments(methodToCopyGenericsFrom, builder.DefineGenericParameters);
3834
}
3935

4036
public static GenericTypeParameterBuilder[] CopyGenericArguments(
4137
MethodInfo methodToCopyGenericsFrom,
42-
MethodBuilder builder,
43-
Dictionary<string, GenericTypeParameterBuilder> name2GenericType)
38+
MethodBuilder builder)
4439
{
45-
return
46-
CopyGenericArguments(methodToCopyGenericsFrom, name2GenericType,
47-
builder.DefineGenericParameters);
48-
}
49-
50-
public static Type ExtractCorrectType(Type paramType, Dictionary<string, GenericTypeParameterBuilder> name2GenericType)
51-
{
52-
if (paramType.IsArray)
53-
{
54-
var rank = paramType.GetArrayRank();
55-
56-
var underlyingType = paramType.GetElementType();
57-
58-
if (underlyingType.IsGenericParameter)
59-
{
60-
GenericTypeParameterBuilder genericType;
61-
if (name2GenericType.TryGetValue(underlyingType.Name, out genericType) == false)
62-
{
63-
return paramType;
64-
}
65-
66-
if (rank == 1)
67-
{
68-
return genericType.MakeArrayType();
69-
}
70-
return genericType.MakeArrayType(rank);
71-
}
72-
if (rank == 1)
73-
{
74-
return underlyingType.MakeArrayType();
75-
}
76-
return underlyingType.MakeArrayType(rank);
77-
}
78-
79-
if (paramType.IsGenericParameter)
80-
{
81-
GenericTypeParameterBuilder value;
82-
if (name2GenericType.TryGetValue(paramType.Name, out value))
83-
{
84-
return value;
85-
}
86-
}
87-
88-
return paramType;
89-
}
90-
91-
public static Type[] ExtractParametersTypes(
92-
ParameterInfo[] baseMethodParameters,
93-
Dictionary<string, GenericTypeParameterBuilder> name2GenericType)
94-
{
95-
var newParameters = new Type[baseMethodParameters.Length];
96-
97-
for (var i = 0; i < baseMethodParameters.Length; i++)
98-
{
99-
var param = baseMethodParameters[i];
100-
var paramType = param.ParameterType;
101-
102-
newParameters[i] = ExtractCorrectType(paramType, name2GenericType);
103-
}
104-
105-
return newParameters;
106-
}
107-
108-
public static Dictionary<string, GenericTypeParameterBuilder> GetGenericArgumentsMap(AbstractTypeEmitter parentEmitter)
109-
{
110-
if (parentEmitter.GenericTypeParams == null || parentEmitter.GenericTypeParams.Length == 0)
111-
{
112-
return new Dictionary<string, GenericTypeParameterBuilder>(0);
113-
}
114-
115-
var name2GenericType = new Dictionary<string, GenericTypeParameterBuilder>(parentEmitter.GenericTypeParams.Length);
116-
foreach (var genType in parentEmitter.GenericTypeParams)
117-
{
118-
name2GenericType.Add(genType.Name, genType);
119-
}
120-
return name2GenericType;
40+
return CopyGenericArguments(methodToCopyGenericsFrom, builder.DefineGenericParameters);
12141
}
12242

12343
private static Type AdjustConstraintToNewGenericParameters(
@@ -184,7 +104,6 @@ private static Type[] AdjustGenericConstraints(MethodInfo methodToCopyGenericsFr
184104

185105
private static GenericTypeParameterBuilder[] CopyGenericArguments(
186106
MethodInfo methodToCopyGenericsFrom,
187-
Dictionary<string, GenericTypeParameterBuilder> name2GenericType,
188107
ApplyGenArgs genericParameterGenerator)
189108
{
190109
var originalGenericArguments = methodToCopyGenericsFrom.GetGenericArguments();
@@ -213,8 +132,6 @@ private static GenericTypeParameterBuilder[] CopyGenericArguments(
213132

214133
newGenericParameters[i].SetGenericParameterAttributes(GenericParameterAttributes.None);
215134
}
216-
217-
name2GenericType[argumentNames[i]] = newGenericParameters[i];
218135
}
219136

220137
return newGenericParameters;

src/Castle.Core/DynamicProxy/Generators/Emitters/MethodEmitter.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
namespace Castle.DynamicProxy.Generators.Emitters
1616
{
1717
using System;
18+
using System.Collections.Generic;
1819
using System.Diagnostics;
1920
using System.Globalization;
2021
using System.Linq;
@@ -57,13 +58,15 @@ internal MethodEmitter(AbstractTypeEmitter owner, string name,
5758
MethodAttributes attributes, MethodInfo methodToUseAsATemplate)
5859
: this(owner, name, attributes)
5960
{
60-
var name2GenericType = GenericUtil.GetGenericArgumentsMap(owner);
61+
// All code paths leading up to this constructor can be traced back to
62+
// proxy type generation code. At present, proxy types are never generic.
63+
Debug.Assert(owner.GenericTypeParams == null || owner.GenericTypeParams.Length == 0);
6164

62-
var returnType = GenericUtil.ExtractCorrectType(methodToUseAsATemplate.ReturnType, name2GenericType);
65+
var returnType = methodToUseAsATemplate.ReturnType;
6366
var baseMethodParameters = methodToUseAsATemplate.GetParameters();
64-
var parameters = GenericUtil.ExtractParametersTypes(baseMethodParameters, name2GenericType);
67+
var parameters = ArgumentsUtil.GetTypes(baseMethodParameters);
6568

66-
genericTypeParams = GenericUtil.CopyGenericArguments(methodToUseAsATemplate, builder, name2GenericType);
69+
genericTypeParams = GenericUtil.CopyGenericArguments(methodToUseAsATemplate, builder);
6770
SetParameters(parameters);
6871
SetReturnType(returnType);
6972
SetSignature(returnType, methodToUseAsATemplate.ReturnParameter, parameters, baseMethodParameters);

src/Castle.Core/DynamicProxy/Internal/TypeUtil.cs

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -98,44 +98,6 @@ public static Type[] GetAllInterfaces(this Type type) // NOTE: also used by Win
9898
return GetAllInterfaces(new[] { type });
9999
}
100100

101-
internal static Type GetClosedParameterType(this AbstractTypeEmitter type, Type parameter)
102-
{
103-
if (parameter.IsGenericTypeDefinition)
104-
{
105-
return parameter.GetGenericTypeDefinition().MakeGenericType(type.GetGenericArgumentsFor(parameter));
106-
}
107-
108-
if (parameter.IsGenericType)
109-
{
110-
var arguments = parameter.GetGenericArguments();
111-
if (CloseGenericParametersIfAny(type, arguments))
112-
{
113-
return parameter.GetGenericTypeDefinition().MakeGenericType(arguments);
114-
}
115-
}
116-
117-
if (parameter.IsGenericParameter)
118-
{
119-
return type.GetGenericArgument(parameter.Name);
120-
}
121-
122-
if (parameter.IsArray)
123-
{
124-
var elementType = GetClosedParameterType(type, parameter.GetElementType());
125-
int rank = parameter.GetArrayRank();
126-
return rank == 1
127-
? elementType.MakeArrayType()
128-
: elementType.MakeArrayType(rank);
129-
}
130-
131-
if (parameter.IsByRef)
132-
{
133-
var elementType = GetClosedParameterType(type, parameter.GetElementType());
134-
return elementType.MakeByRefType();
135-
}
136-
137-
return parameter;
138-
}
139101

140102
public static Type GetTypeOrNull(object target)
141103
{
@@ -232,21 +194,6 @@ internal static bool IsDelegateType(this Type type)
232194
return type.BaseType == typeof(MulticastDelegate);
233195
}
234196

235-
private static bool CloseGenericParametersIfAny(AbstractTypeEmitter emitter, Type[] arguments)
236-
{
237-
var hasAnyGenericParameters = false;
238-
for (var i = 0; i < arguments.Length; i++)
239-
{
240-
var newType = GetClosedParameterType(emitter, arguments[i]);
241-
if (newType != null && !ReferenceEquals(newType, arguments[i]))
242-
{
243-
arguments[i] = newType;
244-
hasAnyGenericParameters = true;
245-
}
246-
}
247-
return hasAnyGenericParameters;
248-
}
249-
250197
private static Type[] Sort(ICollection<Type> types)
251198
{
252199
var array = new Type[types.Count];

0 commit comments

Comments
 (0)