jbevain/cecil — Issue #83: Invalid Metadata generated when changing visibility of nested Type
Author: JohannesRudolph
Created: 2011-11-01 15:44:42+00:00
State: open

Consdering the following code (global namespace) compiled into an assembly:

public class Base
{
public void ProtectableMethod()
{
new Nested();
}
private class Nested
{
public Nested() { }
}
}

When reading the assembly, changing the visibility of Nested to "public" via:

var ad = AssemblyDefinition.ReadAssembly( typeof( Base ).Assembly.Location );
var @base = ad.MainModule.GetType( "Base" );
var nested = @base.NestedTypes.Single();
nested.IsPublic = true;

writing out the assembly and attempting to load it, a BadImageFormatException with the following message is generated: "TypeDef is not marked Nested but has an enclosing type."

Roundtripping the assembly without touching the visibility of the Nested type does not yield this error. Tools such as ILSpy have their problems with the generated assembly (Nested is shown twice, once as a namespace-level class and once as nested). I assume its an error in the generation of metadata.

The full code used to reproduce the issue (Compile as C# console app, add cecil via nuget):

using System;
using System.Collections.Generic;
using Mono.Cecil;
using System.Linq;
using System.Reflection;

public class Base
{
public void ProtectableMethod()
{
new Nested();
}
private class Nested
{
public Nested() { }
}
}

class Program
{
const string AssemblyDumpPath = @"C:\tmp\repro.dll";

static void Main( string[] args )
{
var ad = AssemblyDefinition.ReadAssembly( typeof( Base ).Assembly.Location );

// change the visibility of Nested to public
var @base = ad.MainModule.GetType( "Base" );
var nested = @base.NestedTypes.Single();
nested.IsPublic = true;

// change the assembly name so we can reload the assembly into the same appdomain
ad.Name.Name = "NewAssemblyName";

ad.Write( AssemblyDumpPath );

try
{
var asm = Assembly.LoadFrom( AssemblyDumpPath );
asm.GetType( "Base+Nested" ); // trigger the exception

Console.WriteLine( "Happy days!" );
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}

Console.ReadLine();
}
}