Search code examples
c#clrcilmdbgimetadataimport

Func-eval on polymorphic classes


I'm making a managed .NET debugger using MDBG sample.

MDBG sample operates only on top level class of given instance, not searching deep inside class hierarchy. I was able to go through hierarchy and get all available methods. But a problem occurs in such case:

    public abstract class Base{
        public Base() {SomeProp = "Base"}
        public string SomeProp {get;set;}
    }

    public class A : Base{
        public Base() {SomeProp = "A"}
        public new string SomeProp {get;set;}
    }

    public static void Main(){
        var a = new A();
        var castedToBase = (Base)a;
        //castedToBase.SomeProp -- expect result to be "Base" when debugging
    }

The problem is when I'm getting castedToBase as ICorDebugValue and query for it's ICorDebugValue2::GetExactType I get A class instead of Base class. At that point I cannot distinguish any more which method get_SomeProp to invoke. I would expect ICorDebugValue2::GetExactType to take in consideration performed casts and not return always the underlying type.

How can I understand which method I should invoke?

Some code of what I'm doing now is listed below. mdbgValue represents castedToBase object. szTypedef returns "A" instead of expected "Base"

    IMetadataImport importer;
    var classToken = mdbgValue.CorValue.ExactType.Class.Token;

    int size;
    int ptkExtends;
    TypeAttributes pdwTypeDefFlags;
    importer.GetTypeDefProps(classToken,
        null,
        0,
        out size,
        out pdwTypeDefFlags,
        out ptkExtends
        );
    StringBuilder szTypedef = new StringBuilder(size);
    importer.GetTypeDefProps(classToken,
        szTypedef,
        szTypedef.Capacity,
        out size,
        out pdwTypeDefFlags,
        out ptkExtends
        );

Solution

  • Casting an object to it's base class doesn't change the type of the object, only how it is perceived. I would suggest you need to pass the "perceived" type around along with the value, and use that instead of the actual type for the purpose of finding the correct method.

    The "perceived" type is the statically determined type based on where you got the value from.

    • If you got the value from a parameter using ICorDebugILFrame::GetArgument(), then extract the corresponding argument type from the method signature.
      • If its the first argument and the method signature has the HasThis flag but not the ExplicitThis flag then get the type from the value instead.
    • If you got the value from a local using ICorDebugILFrame::GetLocalVariable() then extract the type from the methods locals signature (the metadata token of the locals signature needs to be extracted from the method header.)
    • If you got the value from running a method with ICorDebugEval (eg. a property getter), then you should use the return type of the method you called (also extracted from the method signature.)
    • If you got the value from a field, then extract the type from the field signature.
    • If you cast a value, then use whatever type you are casting to.