1

I have a set of disjoint dynamically written classes, I want to compile (and fix compilation errors) individually. Then I want to combine them into the same assembly. Since they have been already compiled, I do not want to simply pass their syntax trees to the assembly compilation (in order to avoid compiling twice). I was trying to compile them into netmodules, and then use those netmodules as reference to an assembly but the resulting assembly does not have the required types.

Here is my test code (very similar to Roslyn's CompilationEmitTests.MultipleNetmodulesWithPrivateImplementationDetails())

public static class Program
{

    public static void Main()
    {
        var s1 = @"public class A {internal object o1 = new { hello = 1, world = 2 }; public static string M1() {    return ""Hello, "";}}";
        var s2 = @"public class B : A{internal object o2 = new { hello = 1, world = 2 };public static string M2(){    return ""world!"";}}";
        var s3 = @"public class Program{public static void Main(){    System.Console.Write(A.M1());    System.Console.WriteLine(B.M2());}}";
        var comp1 = CreateCompilationWithMscorlib("a1", s1, compilerOptions: TestOptions.ReleaseModule);
        var ref1 = comp1.EmitToImageReference();

        var comp2 = CreateCompilationWithMscorlib("a2", s2, compilerOptions: TestOptions.ReleaseModule, references: new[] { ref1 });
        var ref2 = comp2.EmitToImageReference();

        var comp3 = CreateCompilationWithMscorlib("a3", s3, compilerOptions: TestOptions.ReleaseExe.WithModuleName("C"), references: new[] { ref1, ref2 });

        var ref3 = comp3.EmitToImageReference();

        IEnumerable<byte> result = comp3.EmitToArray();

        Assembly assembly = Assembly.Load(result.ToArray());

        Module module = assembly.GetModule("C");

        Type prog = module.GetType("Program");

        object instance = Activator.CreateInstance(prog);

        MethodInfo method = prog.GetMethod("Main");

        method.Invoke(null, null);
    }

    public static ImmutableArray<byte> ToImmutable(this MemoryStream stream)
    {
        return ImmutableArray.Create<byte>(stream.ToArray());
    }

    internal static ImmutableArray<byte> EmitToArray
    (
        this Compilation compilation,
        EmitOptions options = null,
        Stream pdbStream = null
    )
    {
        var stream = new MemoryStream();

        if (pdbStream == null && compilation.Options.OptimizationLevel == OptimizationLevel.Debug)
        {
            pdbStream = new MemoryStream();
        }

        var emitResult = compilation.Emit(
            peStream: stream,
            pdbStream: pdbStream,
            xmlDocumentationStream: null,
            win32Resources: null,
            manifestResources: null,
            options: options);

        return stream.ToImmutable();
    }
    public static MetadataReference EmitToImageReference(
        this Compilation comp
    )
    {
        var image = comp.EmitToArray();
        if (comp.Options.OutputKind == OutputKind.NetModule)
        {
            return ModuleMetadata.CreateFromImage(image).GetReference(display: comp.AssemblyName);
        }
        else
        {
            return AssemblyMetadata.CreateFromImage(image).GetReference(display: comp.AssemblyName);
        }
    }

    private static CSharpCompilation CreateCompilationWithMscorlib(string assemblyName, string code, CSharpCompilationOptions compilerOptions = null, IEnumerable<MetadataReference> references = null)
    {
        SourceText sourceText = SourceText.From(code, Encoding.UTF8);
        SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, null, "");

        MetadataReference mscoreLibReference = AssemblyMetadata.CreateFromFile(typeof(string).Assembly.Location).GetReference();

        IEnumerable<MetadataReference> allReferences = new MetadataReference[] { mscoreLibReference };

        if (references != null)
        {
            allReferences = allReferences.Concat(references);
        }

        CSharpCompilation compilation = CSharpCompilation.Create
        (
            assemblyName,
            new[] {syntaxTree},
            options : compilerOptions,
            references: allReferences
        );

        return compilation;
    }

}

The Main() throws an exception at static method Invoke() because it cannot find the reference to A and B classes.

What am I doing wrong?

svick
  • 236,525
  • 50
  • 385
  • 514
Nick Polyak
  • 201
  • 1
  • 9
  • It would probably be helpful to drop in the stack trace. – muglio May 13 '16 at 15:32
  • the dynamic program (defined by string s3) cannot find references to A and B (located in netmodules defined in strings s1 and s2). There is nothing Roslyn specific. – Nick Polyak May 13 '16 at 15:39
  • 1
    If anyone else is interested, the solution is here: https://github.com/dotnet/roslyn/issues/11297. – svick May 14 '16 at 00:36
  • @svick I also have similar issue in https://stackoverflow.com/questions/46825603/add-module-as-reference-in-roslyn Appreciate if you can help me on this. – Bandara Oct 19 '17 at 08:49

1 Answers1

0

Here is the answer that user 0xd4d posted on Github for the same question. I tested that it works:

You must call LoadModule before the netmodules need to be accessed

assembly.LoadModule("a1.netmodule", a1_netmodule_bytes);
assembly.LoadModule("a2.netmodule", a2_netmodule_bytes);

If you've saved them all to disk, you don't need to do this.

I still do not understand what he meant by stating that if I saved it to the disk I would not need to do this.

Nick Polyak
  • 201
  • 1
  • 9