Developer to Developer: Help needed emitting generic method calls

14 views
Skip to first unread message

Avish

unread,
Nov 11, 2006, 1:16:36 PM11/11/06
to Nemerle Forum
Hi. I'm Avish and I'm a developer over at Boo
(http://boo.codehaus.org). I came across a strange problem trying to
make the Boo compiler emit calls to methods defined on generic types,
and I couldn't find anything to help me with it. However, during my
search for answers I found that the Nemerle compiler uses
System.Reflection.Emit and has full generics support, so I guess you
guys must've solved this at some point.
I contemplated looking through the code but I can't read Nemerle syntax
fast enough and I'd just get lost in there, so I thought I'd ask you
guys.

I'm sorry if this is extremely long and unintelligible. Unfortunately
the .NET terminology for generics makes any text concerning generics
extremely confusing.

The problem is as follows:

Suppose we have the following setup: a generic class, Base<>, which
declares a (non-generic) method Foo(); and a derived class Derived<>
which derives from Base<> with the same argument (i.e. class Derived<T>
: Base<T>).
These types are external (i.e. already compiled), and we're trying to
compile code that instantiates the type Dervied<MyClass> (from
Derived<>) and calls Foo on it.

Since MyClass is an internal class, when the compiler calls
MakeGenericType() on Derived<>, passing MyClass as the argument, it
gets us a TypeBuilderInstantiation representing Derived<MyClass>.
In order to get the MethodInfo of the Foo method on this constructed
type, we should be able to use TypeBuilder.GetMethod(typeof(Derived<>),
fooMethodInfo), and emit that.

The problem is that TypeBuilder.GetMethod only works if you have the
MethodInfo for the generic type definition (i.e. Base<>.Foo). The
MethodInfo we have is the one from the open constructed type from which
Derived<> inherits - we have the MethodInfo for Base<T>.Foo, where T is
the generic parameter of Derived.

TypeBuilder.GetMethod() doesn't work in this scenario. What I need is a
way to acquire the MethodInfo of Base<>.Foo (the one in the generic
type definition) from the method info of Base<T>.Foo (the one in the
open constructed type). In essence, I need to perform the inverse of
TypeBuilder.GetMethod() - map a method from a constructed version to
the method from the definition.

For a concrete example, try to compile the Nemerle equivalent of this:

using System.ComponentModel;

public class Person
{
public string Name;
}

BindingList<Person> list = new BindingList<Person>();
list.Add(new Person());
Person p = list[0];

Since BindingList<> inherits the special method get_Item(int) from
System.Collections.ObjectModel.Collection<>, trying to emit the last
line gives us the above mentioned problem.

I'm pretty sure you guys must have solved this problem since it seems
critical for any generics-enabled compiler. Can you help me out here?

Thanks,
Avish

Kamil Skalski

unread,
Nov 11, 2006, 1:47:44 PM11/11/06
to nemer...@googlegroups.com
Well, I guess you entered the field full of Microsoft's ugly bugs. But
you are lucky as we have got around most of them, though you will see
how hackish it was...

The solution to the problem (if it is really what I'm thinking about)
http://nemerle.org/svn/nemerle/trunk/ncc/generation/ILEmitter.n

So, suppose we have
class Foo [T] { public Meth () : void { } }
class Bar [T] : Foo [T] { }

and we want to call Bar[int].Meth();

We do following:
- get instantiated declaring type of Meth ---> Foo[int]
(we do it on our internal compiler structures, so effectively we
takie generic declaring type for Foo and then call MakeGenericType
(typeof(int)) on it)
- find Meth in it

Pretty simple, isn't it? But here the problems with MS.NET begins.
Finding method in instantiated generic type is not easy, especially if
you have to deal with both internal and external types.

Here is a cut from ILEmitter.n with crucial methods, if you have more
questions about it, please ask:

static GetMethodInfo (from_type : TyVar, meth : IMethod, typars :
list [TyVar]) : MethodInfo
{
mutable meth_inf = meth.GetMethodInfo ();

// here we fetch the proper instantiation of meth's declaring type
def from_type = from_type.Fix ().GetInstantiatedSuperType
(meth.DeclaringType);
def system_from_type = from_type.SystemType;

when (system_from_type.IsGenericType)
meth_inf = FrameworkGetMethod (system_from_type, meth_inf);
....


static FrameworkGetMethod (t : System.Type, mutable m :
MethodInfo) : MethodInfo
{
if (MS_NET_RuntimeType != null)
if (t.GetType().Equals (MS_NET_RuntimeType))
// workaround MS.NET limitation of not allowing RuntimeType
in TypeBuilder.GetMethod
GetHackishMethod (t, m);
else {
def td = t.GetGenericTypeDefinition ();
when (td.GetType().Equals (MS_NET_RuntimeType))
m = GetHackishMethod (td, m);

TypeBuilder.GetMethod (t, m);
}
else
TypeBuilder.GetMethod (t, m);
}


static GetHackishMethod (_from_type : System.Type, meth :
MethodInfo) : MethodInfo
{
def curtok =
if (meth is Emit.MethodBuilder)
typeof (Emit.MethodBuilder).GetProperty
("MetadataTokenInternal", BindingFlags.NonPublic |
BindingFlags.Instance).GetValue (meth, null) :> int;
else
meth.MetadataToken;

res: {
foreach (m in _from_type.GetMethods (BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.DeclaredOnly))
when (m.MetadataToken == curtok)
res (m);
throw System.Exception ($"not found: $meth in $_from_type")
}
}


As you probably noticed, the "hack" sections are only performed in
MS_RuntimeType is not null. On mono it all works without problems :)


--
Kamil Skalski
http://nazgul.omega.pl

Avish

unread,
Nov 11, 2006, 2:48:11 PM11/11/06
to nemer...@googlegroups.com
Thanks. I don't understand all of it but I stumbled upon it myself, minutes after posting my original question. I implemented something similar in the Boo compiler and it seems to work - thanks!

I have some questions though. You use GetHackishMethod only if all of these conditions are met:
1) You're running under MS.NET and not Mono
2) The type declaring the method is generic (either a generic definition or a constructed type)
3) Either the declaring type or its generic type definition are runtime types.

I implemented rule #2 and it seems to be working. Rule #1 I can't test, as I only run MS.NET, but I guess no harm will be done if the hack will be applied on Mono as well. However I can't understand the reason for rule #3 - does TypeBuilder.GetMethod work for non-runtime types, such as TypeBuilder and TypeBuilderInstantiation?
Boo doesn't yet have support for defining internal generics so I don't really know how the framework behaves in such a case.

Also, is there any sort of bug report or issue regarding this matter over at Microsoft?

In any case, thanks for the quick reply. I appreciate the help!

Kamil Skalski

unread,
Nov 11, 2006, 3:02:32 PM11/11/06
to nemer...@googlegroups.com
On 11/11/06, Avish <some....@gmail.com> wrote:
> Thanks. I don't understand all of it but I stumbled upon it myself, minutes
> after posting my original question. I implemented something similar in the
> Boo compiler and it seems to work - thanks!
>
> I have some questions though. You use GetHackishMethod only if all of these
> conditions are met:
> 1) You're running under MS.NET and not Mono
> 2) The type declaring the method is generic (either a generic definition or
> a constructed type)
> 3) Either the declaring type or its generic type definition are runtime
> types.
>
> I implemented rule #2 and it seems to be working. Rule #1 I can't test, as I
> only run MS.NET, but I guess no harm will be done if the hack will be
> applied on Mono as well. However I can't understand the reason for rule #3 -
> does TypeBuilder.GetMethod work for non-runtime types, such as TypeBuilder
> and TypeBuilderInstantiation?

Something like this... I don't remember exactly all the cases when it
failed, but current version was created after long tuning of
conditions - in a matter of fact, in each condition different approach
need to be applied, none of them is universal.
When you define internal generic types there are also additional
issues with order of calling CreateType and using its result instead
of original TypeBuilders.

> Boo doesn't yet have support for defining internal generics so I don't
> really know how the framework behaves in such a case.
>
> Also, is there any sort of bug report or issue regarding this matter over at
> Microsoft?

At least two are there:
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94516
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=97424

But both were marked as "by design" or "closed", so I guess they won't be fixed.


>
> In any case, thanks for the quick reply. I appreciate the help!
>

Good luck with next testcases :)

Avish

unread,
Nov 11, 2006, 4:29:10 PM11/11/06
to nemer...@googlegroups.com
So I'll probably bug you guys again when we get to doing internal generics. :)
Thanks again!

  Avish

> > > ( http://boo.codehaus.org). I came across a strange problem trying to
Reply all
Reply to author
Forward
0 new messages