Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Match generic type parameters by position, not by name #465

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,13 @@
# Castle Core Changelog

## Unreleased

Bugfixes:
- Generic method with differently named generic arguments to parent throws `KeyNotFoundException` (@stakx, #106)

Deprecations:
- `Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.GetGenericArgumentsFor(Type)` (method)

## 4.4.0 (2019-04-05)

Enhancements:
Expand Down
1 change: 1 addition & 0 deletions ref/Castle.Core-net35.cs
Expand Up @@ -3267,6 +3267,7 @@ public void DefineCustomAttributeFor<TAttribute>(Castle.DynamicProxy.Generators.
public System.Collections.Generic.IEnumerable<Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference> GetAllFields() { }
public Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference GetField(string name) { }
public System.Type GetGenericArgument(string genericArgumentName) { }
[System.ObsoleteAttribute("Intended for internal use only.")]
public System.Type[] GetGenericArgumentsFor(System.Type genericType) { }
public System.Type[] GetGenericArgumentsFor(System.Reflection.MethodInfo genericMethod) { }
public void SetGenericTypeParameters(System.Reflection.Emit.GenericTypeParameterBuilder[] genericTypeParameterBuilders) { }
Expand Down
1 change: 1 addition & 0 deletions ref/Castle.Core-net40.cs
Expand Up @@ -3316,6 +3316,7 @@ public void DefineCustomAttributeFor<TAttribute>(Castle.DynamicProxy.Generators.
public System.Collections.Generic.IEnumerable<Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference> GetAllFields() { }
public Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference GetField(string name) { }
public System.Type GetGenericArgument(string genericArgumentName) { }
[System.ObsoleteAttribute("Intended for internal use only.")]
public System.Type[] GetGenericArgumentsFor(System.Type genericType) { }
public System.Type[] GetGenericArgumentsFor(System.Reflection.MethodInfo genericMethod) { }
public void SetGenericTypeParameters(System.Reflection.Emit.GenericTypeParameterBuilder[] genericTypeParameterBuilders) { }
Expand Down
1 change: 1 addition & 0 deletions ref/Castle.Core-net45.cs
Expand Up @@ -3316,6 +3316,7 @@ public void DefineCustomAttributeFor<TAttribute>(Castle.DynamicProxy.Generators.
public System.Collections.Generic.IEnumerable<Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference> GetAllFields() { }
public Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference GetField(string name) { }
public System.Type GetGenericArgument(string genericArgumentName) { }
[System.ObsoleteAttribute("Intended for internal use only.")]
public System.Type[] GetGenericArgumentsFor(System.Type genericType) { }
public System.Type[] GetGenericArgumentsFor(System.Reflection.MethodInfo genericMethod) { }
public void SetGenericTypeParameters(System.Reflection.Emit.GenericTypeParameterBuilder[] genericTypeParameterBuilders) { }
Expand Down
1 change: 1 addition & 0 deletions ref/Castle.Core-netstandard1.3.cs
Expand Up @@ -2145,6 +2145,7 @@ public void DefineCustomAttributeFor<TAttribute>(Castle.DynamicProxy.Generators.
public System.Collections.Generic.IEnumerable<Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference> GetAllFields() { }
public Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference GetField(string name) { }
public System.Type GetGenericArgument(string genericArgumentName) { }
[System.ObsoleteAttribute("Intended for internal use only.")]
public System.Type[] GetGenericArgumentsFor(System.Type genericType) { }
public System.Type[] GetGenericArgumentsFor(System.Reflection.MethodInfo genericMethod) { }
public void SetGenericTypeParameters(System.Reflection.Emit.GenericTypeParameterBuilder[] genericTypeParameterBuilders) { }
Expand Down
1 change: 1 addition & 0 deletions ref/Castle.Core-netstandard1.5.cs
Expand Up @@ -2145,6 +2145,7 @@ public void DefineCustomAttributeFor<TAttribute>(Castle.DynamicProxy.Generators.
public System.Collections.Generic.IEnumerable<Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference> GetAllFields() { }
public Castle.DynamicProxy.Generators.Emitters.SimpleAST.FieldReference GetField(string name) { }
public System.Type GetGenericArgument(string genericArgumentName) { }
[System.ObsoleteAttribute("Intended for internal use only.")]
public System.Type[] GetGenericArgumentsFor(System.Type genericType) { }
public System.Type[] GetGenericArgumentsFor(System.Reflection.MethodInfo genericMethod) { }
public void SetGenericTypeParameters(System.Reflection.Emit.GenericTypeParameterBuilder[] genericTypeParameterBuilders) { }
Expand Down
@@ -0,0 +1,53 @@
// Copyright 2004-2019 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.DynamicProxy.Tests
{
using System;

using Castle.DynamicProxy.Tests.Interceptors;

using NUnit.Framework;

[TestFixture]
public class GenericTypeParameterNameMismatchTestCase : BasePEVerifyTestCase
{
[Test]
[TestCase(typeof(Test))]
[TestCase(typeof(TestVirtual))]
public void GenericMethodDifferentlyNamedGenericArguments(Type classType)
{
generator.CreateClassProxy(classType, new[] { typeof(ITest) }, new DoNothingInterceptor());
}

public interface ITest
{
void Hi<T>();
}

public class Test : ITest
{
public void Hi<U>()
{
}
}

public class TestVirtual : ITest
{
public virtual void Hi<U>()
{
}
}
}
}
Expand Up @@ -279,6 +279,7 @@ public Type GetGenericArgument(String genericArgumentName)
return null;
}

[Obsolete("Intended for internal use only.")] // TODO: Remove this method.
public Type[] GetGenericArgumentsFor(Type genericType)
{
var types = new List<Type>();
Expand All @@ -303,7 +304,7 @@ public Type[] GetGenericArgumentsFor(MethodInfo genericMethod)
var types = new List<Type>();
foreach (var genType in genericMethod.GetGenericArguments())
{
types.Add(name2GenericType[genType.Name].AsType());
types.Add(genericTypeParams[genType.GenericParameterPosition].AsType());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense. I wonder if it would be a good idea to check if genericTypeParams isn't null and the array contains the element before accessing it since it is provided externally by SetGenericTypeParameters, then provide a useful exception, so we don't get something useless like the previous KeyNotFoundException one.

Have you looked at the previous GetGenericArgumentsFor overload to see if that should also be changed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would be a good idea to check if genericTypeParams isn't null and the array contains the element before accessing it since it is provided externally by SetGenericTypeParameters

Having an assertion to that effect might be good, but it shouldn't be any more necessary than checking whether name2GenericType contains the key genType.Name. Note how the only place where SetGenericTypeParameters is called from initializes both genericTypeParams and populates name2GenericType:

SetGenericTypeParameters(GenericUtil.CopyGenericArguments(methodToCopyGenericsFrom, typebuilder, name2GenericType));

I'll give it some more thought.

This comment was marked as resolved.

Copy link
Member Author

@stakx stakx Aug 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you looked at the previous GetGenericArgumentsFor overload to see if that should also be changed?

@jonorossi, it turns out that other overload is most probably unreachable code. See the new commit for details. Without any test cases, it's hard to know whether or not it is faulty. But given that it's public and there may in theory be external consumers, I think the safest option is to leave it as is, but mark it [Obsolete].

We have yet another method, Type GetGenericArgument(string genericArgumentName), which precedes the two that we've discussed so far. The code paths surrounding generic type arguments are rather convoluted, and I don't understand them well enough (yet) to know whether or not there needs to be a change here, too.

}

return types.ToArray();
Expand Down
8 changes: 7 additions & 1 deletion src/Castle.Core/DynamicProxy/Internal/TypeUtil.cs
Expand Up @@ -102,7 +102,13 @@ public static Type GetClosedParameterType(this AbstractTypeEmitter type, Type pa
{
if (parameter.GetTypeInfo().IsGenericTypeDefinition)
{
return parameter.GetGenericTypeDefinition().MakeGenericType(type.GetGenericArgumentsFor(parameter));
// If my understanding of `.IsGenericTypeDefinition` is correct, we arrive here
// only if `parameter` is not a fully constructed (closed) generic type... which
// according to ECMA-335, section II.9.4, shouldn't be possible:
//
// "The CLI does not support partial instantiation of generic types. And generic
// types shall not appear uninstantiated anywhere in metadata signature blobs."
throw new NotSupportedException();
}

if (parameter.GetTypeInfo().IsGenericType)
Expand Down