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?