MonoCecil giving the wrong method name when reading an F# binary by reflection

67 views Asked by At

I have a net 6 F# binary that contains some methods, on which some have one or many [<TestCategory(CategoryName)>] attribute defined on them. My goal is to use reflection, from a C# application, to read and gather the fully qualified names of those test methods.

In order to achieve this, I am trying to use the Mono.Cecil library in order to read the information from the F# binary. This seems to work pretty well with C# binaries, but not so much with F# ones. The problem I am encountering is that the fully qualified name (and name) properties of the method Mono.Cecil gives me is under the format "BinaryName.ModuleName.f@LineNumber", instead of giving me "BinaryName.ModuleName.ActualMethodName".

Here is how I get the information via reflection using Mono.Cecil.

    private static IEnumerable<GenericModule> ReadModulesRecursively(string assemblyPath)
    {
        var modules = new List<GenericModule>();

        var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath);
        foreach (var module in assemblyDefinition.Modules)
        {
            var moduleTypes = module.Types.Select(ToGenericType); // Construct a DTO containing only what I want
            modules.Add(new GenericModule(module.Name, moduleTypes));
        }

        return modules;
    }

    private static GenericType ToGenericType(TypeDefinition typeDefinition)
    {
        return new GenericType(typeDefinition.Name, typeDefinition.CustomAttributes.Select(ToGenericAttribute), typeDefinition.Methods.Select(ToGenericMethod));
    }

    private static GenericMethod ToGenericMethod(MethodDefinition methodDefinition)
    {
        return new GenericMethod(methodDefinition.Name, $"{methodDefinition.DeclaringType.FullName}.{methodDefinition.Name}", methodDefinition.DeclaringType.FullName,
            methodDefinition.CustomAttributes.Select(ToGenericAttribute));
    }

And here is an example of a F# method declaration I am trying to get information on, in the F# binary:

module myModule

<open statements...>

[<TestClass>]
[<DeploymentItem("Deploy1")>]
[<DeploymentItem("Deploy2")>]
type Tests ()=

    [<TestMethod>]
    [<TestCategory("Cat1")>]
    [<TestCategory("Cat2")>]
    member x.``This is a test title`` () =
        ()

So the problem here seems to be that the MonoCecil MethodDefinition type doesn't contain the right Name and FullName property values.

Is this a known issue with MonoCecil and F#, or am I doing something wrong?

1

There are 1 answers

0
Brian Berns On

You haven't given us enough of your code to reproduce what you're seeing, but this short test seems to work correctly (or at least differently from what you're describing):

var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath);
var myModule = assemblyDefinition.MainModule.Types
    .Where(type => type.Name == "myModule")
    .Single();
var testsType = myModule.NestedTypes.Single();
var testMethod = testsType.Methods
    .Where(method => method.Name == "This is a test title")
    .Single();
Console.WriteLine($"Name: {testMethod.Name}");   // "This is a test title"
Console.WriteLine($"Full name: {testMethod.FullName}");   // "System.Void myModule/Tests::This is a test title()"

Note that the method's Name property is "This is a test title", as expected. The FullName also looks correct, although I can't say for certain.

Neither of the outputs follow the pattern you describe, so I suspect the problem is actually in your code. However, without seeing more of your program, it's hard to say for sure.

It's important to understand that the F# compiler's idea of a module doesn't necessarily correspond to what you might expect. In particular, the F# myModule module gets compiled into a type that then contains a nested Tests type.

Related Questions in F#