Donate Share

RunSharp

Tracker: Bugs

5 Bug (and workaround) calling virtual functions on value type - ID: 1864084
Last Update: Comment added ( pista )

(replicated from:
http://www.codeproject.com/KB/dotnet/runsharp.aspx?msg=2378379#xx2378379xx)


Hi, first of all - great work on this!

I had a problem trying to call GetHashCode() on an int, here's an example:

public delegate int Int32Delegate();

static void Main(string[] args)
{
DynamicMethodGen dmg =
DynamicMethodGen.Static(typeof(Program)).Method(typeof(int));
CodeGen g = dmg.Code;
{
Operand value = g.Local(typeof(int), 0);
g.Return(value.Invoke("GetHashCode"));
}

DynamicMethod dm = dmg.GetCompletedDynamicMethod(true);

Int32Delegate myfunc =
(Int32Delegate)dm.CreateDelegate(typeof(Int32Delegate));
Console.WriteLine(myfunc()); // causes NullReferenceException
}


Unfortunately, when the delegate gets called the generated code throws and
exception. If I output the same function to a dll and run it through
peverify.exe I get the following:

[IL]: Error: [hello.dll : MyClass::DoIt][offset 0x00000004][found address
of Int32][expected ref 'System.Object'] Unexpected type on the stack.
[IL]: Error: [hello.dll : MyClass::DoIt][offset 0x00000004] Call to base
type of valuetype.


Ok, the IL looks unhappy for some reason. Here's the line in question:

IL_0004: callvirt instance int32 [mscorlib]System.Object::GetHashCode()


After some digging around I find the following IL is ok:

IL_0004: call instance int32 [mscorlib]System.Int32::GetHashCode()


Ok, looks like the IL being generated via Invoke() doesn't seem to like
calling virtual functions (like GetHashCode()) that are defined on value
types. callvirt is expecting to see a System.Object but barfs as it's
getting a System.Int32 instead.

If you need a workaround, you can cast the value type to an object (at
least in this instance.) I've come up with a patch that generates IL that
passes peverify, but I've no idea if it breaks anything else. Here it is:

--- dist/RunSharp/Operand.cs Mon Nov 05 22:00:36 2007
+++ dist/RunSharp/Operand.cs Fri Jan 04 15:12:50 2008
@@ -70,7 +70,7 @@

internal virtual bool TrivialAccess { get { return false; } }
internal virtual bool IsStaticTarget { get { return false; } }
- internal virtual bool SuppressVirtual { get { return false; } }
+ internal virtual bool SuppressVirtual { get { return Type != null &&
Type.IsValueType; } }
internal virtual object ConstantValue { get { return null; } }
internal virtual void AssignmentHint(Operand op) { }
#endregion
--- dist/RunSharp/TypeInfo.cs Mon Nov 05 22:00:36 2007
+++ dist/RunSharp/TypeInfo.cs Fri Jan 04 15:15:37 2008
@@ -345,7 +345,7 @@
{
for (; t != null; t = t.BaseType)
{
- ApplicableFunction af = OverloadResolver.Resolve(Filter(GetMethods(t),
name, false, @static, false), args);
+ ApplicableFunction af = OverloadResolver.Resolve(Filter(GetMethods(t),
name, false, @static, !@static), args);

if (af != null)
return af;


This can be applied the the 0.1.1 pre-alpha version that sits on
SourceForge at the moment.

cheers,
ninj


Nobody/Anonymous ( nobody ) - 2008-01-04 17:38

5

Closed

Fixed

Stefan Simek

None

None

Public


Comments ( 3 )

Date: 2009-08-07 22:29
Sender: pistaProject Admin

the bug is fixed in the hg repository, fix will be included in next release


Date: 2008-01-07 16:27
Sender: nobody

Logged In: NO

your new patch seems to work for me! I wish I knew more about IL, and
thanks for your quick response! :)

thanks,
Steve

ps. http://www.codeproject.com/KB/msil/emithelper.aspx looks like a nice
way to emit IL should you understand it (which I don't!)


Date: 2008-01-06 22:15
Sender: pistaProject Admin


The problem here is that virtual calls to value-types are not handled
properly. Quite a lot can be found in the MSDN documentation for the
'constrained' instruction.

Fix proposal:
Add a 'constrained' instruction before the calls to value-type virtuals.

Here's the diff (also attached):

==== //triaxis/projects/RunSharp/0.1-rel/RunSharp/CodeGen.Helpers.cs#1 -
P:\projects\RunSharp\0.1-rel\RunSharp\CodeGen.Helpers.cs ====
@@ -371,7 +371,13 @@
MethodInfo mi = mth as MethodInfo;
if (mi != null)
{
- il.Emit((((object)target != null && target.SuppressVirtual) ||
mi.IsStatic) ? OpCodes.Call : OpCodes.Callvirt, mi);
+ bool suppressVirtual = ((object)target != null &&
target.SuppressVirtual) || mi.IsStatic;
+
+ if (!suppressVirtual && (object)target != null &&
target.Type.IsValueType && mi.IsVirtual)
+ {
+ il.Emit(OpCodes.Constrained, target.Type);
+ }
+ il.Emit(suppressVirtual ? OpCodes.Call : OpCodes.Callvirt, mi);
return;
}

After the fix is tested, it will be included in the 0.1.2-pre-alpha
version.

Regards,
Stefan
File Added: runsharp-0.1.1-pre-alpha-value-virt.patch


Attached Files ( 2 )

Filename Description Download
runsharp-0.1.1-pre-alpha-patch-sw.patch unified diff patch file Download
runsharp-0.1.1-pre-alpha-value-virt.patch patch proposal (unified diff) Download

Changes ( 13 )

Field Old Value Date By
status_id Open 2009-08-07 22:29 pista
close_date - 2009-08-07 22:29 pista
allow_comments 1 2009-08-07 22:29 pista
resolution_id None 2009-08-07 22:29 pista
status_id Pending 2008-01-06 22:23 pista
resolution_id Accepted 2008-01-06 22:23 pista
close_date 2008-01-06 22:16 2008-01-06 22:23 pista
status_id Open 2008-01-06 22:16 pista
assigned_to nobody 2008-01-06 22:16 pista
resolution_id None 2008-01-06 22:16 pista
close_date - 2008-01-06 22:16 pista
File Added 261099: runsharp-0.1.1-pre-alpha-value-virt.patch 2008-01-06 22:15 pista
File Added 260790: runsharp-0.1.1-pre-alpha-patch-sw.patch 2008-01-04 17:38 nobody