C# Roslyn to use previously compiled class in next compilations - c#

I ask with example,
Lets say I have the following code.
fullcommand = #"public class oldTest
{
public static void oldTestMethod(){
Console.WriteLine(""oldTest Class"");
}
}"
var syntaxTree = CSharpSyntaxTree.ParseText(fullCommand);
var compilation = CSharpCompilation.Create(
assemblyName,
new[] {syntaxTree},
references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,allowUnsafe:true));
var ms = new MemoryStream();
var result = compilation.Emit(ms);
And I will compile the above code with Roslyn in memory.
next i want to compile another code in memory to use the above compiled class, lets say the below.
new_fullcommand = #"public class newTest
{
public static void newTest(){
oldTest.oldTestMethod();
}
}"
var syntaxTree = CSharpSyntaxTree.ParseText(new_fullcommand);
var compilation = CSharpCompilation.Create(
assemblyName,
new[] {syntaxTree},
references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,allowUnsafe:true));
var ms = new MemoryStream();
var result = compilation.Emit(ms);
how can i make the second code to use the first code as its reference? or use it?

The easiest way would probably be to pass in multiple syntaxTree objects when you create your compilation.
However, if you want to build up your compilation incrementally I believe you can use Compilation.AddSyntaxTrees to your first compilation object.

Related

What's the best way to load a CSX script from a C# application?

Tools like dotnet-script and CSI allow users to write, compile, and run C# like "scripts" rather than including their code in a complete pre-compiled project. These tools work great for command-line usage, but don't offer much in terms of integrating dynamic C# "scripts" into a larger C# application.
If I have an existing C# application which wishes to load additional classes into its existing namespaces via .csx "scripts", how do I do that? Is it possible?
I guess you need to compile and execute your C# script.
In my experience, I used C# scripts by referencing Microsoft.CodeAnalysis.CSharp.Scripting (version 3.*) directly.
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.*" />
Compilation
I suggest to use default compilation options:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Scripting;
// ...
ScriptOptions options = ScriptOptions.Default;
Maybe in the future, you'll need to add referenced assemblies to your script compilation.
So you need to compile your script (contained in a string variable in the code below):
byte[] assemblyBinaryContent;
var roslynScript = CSharpScript.Create(script, options);
var compilation = roslynScript.GetCompilation();
compilation = compilation.WithOptions(compilation.Options
.WithOptimizationLevel(OptimizationLevel.Release)
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary));
using (var assemblyStream = new MemoryStream())
{
var result = compilation.Emit(assemblyStream);
if (!result.Success)
{
var errors = string.Join(Environment.NewLine, result.Diagnostics.Select(x => x));
throw new Exception("Compilation errors: " + Environment.NewLine + errors);
}
assemblyBinaryContent = assemblyStream.ToArray();
}
GC.Collect(); // it allows to force clear compilation stuff.
Assembly assembly = Assembly.Load(assemblyBinaryContent);
var ret = Run(assembly); // see next paragraph
Execution
Obviously you need an entry point to execute your script.
I found out this trickly solution. It works.
private object Run(Assembly assembly)
{
//Execute the script
var type = assembly.GetType("Submission#0");
var method = type.GetMethod("<Factory>", BindingFlags.Static | BindingFlags.Public);
var retTask = method.Invoke(null, new object[] { new object[2] }) as Task<object>;
return retTask.GetAwaiter().GetResult();
}
I hope it can help.

Embedded files with Roslyn compilation

I am looking for an example in how to compile a project using Roslyn. The code below is an example I found in https://github.com/dotnet/roslyn/wiki/FAQ … This examples doesn't cover embedded files. Is that possible?
public class MyTask : Task {
public override bool Execute() {
var projectFileName = this.BuildEngine.ProjectFileOfTaskNode;
var project = ProjectCollection.GlobalProjectCollection.
GetLoadedProjects(projectFileName).Single();
var compilation = CSharpCompilation.Create(
project.GetPropertyValue("AssemblyName"),
syntaxTrees: project.GetItems("Compile").Select(
c => SyntaxFactory.ParseCompilationUnit(
c.EvaluatedInclude).SyntaxTree),
references: project.GetItems("Reference")
.Select(
r => new MetadataFileReference
(r.EvaluatedInclude)));
// Now work with compilation ...
}
}
Yes, it is possible to embed resources into result assembly using Roslyn.
To produce a result assembly CSharpCompilation type has an Emit method. This method has many parameters. One of them is manifestResources, which is responsible for adding embedded resources. You mas specify as many resources as you want. The following code demonstrates how you can use this parameter to emit an assembly with an embedded resource into peStream. It creates a resource with name "resourceName" and content that located at "path-to-resource" path.
void ProduceAssembly(CSharpCompilation compilation, Stream peStream)
{
ResourceDescription[] resources =
{
new ResourceDescription(
"resourceName",
() => File.OpenRead("path-to-resource"),
isPublic: true
)
};
var result = compilation.Emit(peStream, manifestResources: resources);
if (!result.Success)
{
var diagnostics = string.Join(Environment.NewLine, result.Diagnostics);
throw new Exception($"Compilation failed with: {diagnostics}");
}
}
Don't forget to check EmitResult.Success property to ensure that compilation completed successfully. Also ensure that peStream is disposed properly after compilation.

Roslyn: Compile expression with non-public fields from external assembly

I am working on evaluating of user expressions from debugger. I want to compile expression in method context, and then inject IL-code with debugger.
Is it possible to compile expression, which contains non-public class/class-fields from external assembly to IL-code with Roslyn?
I've got 'MyNamespace.dll' with public class 'Test' and private method 'PrivateMethod', and I want to call it from Roslyn compilation.
I am trying to do it with next code:
public class TestCompilationOptions
{
public void Test()
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "Output.dll");
Console.WriteLine("Preparing syntax tree");
string expressionString = #"
using System;
class XXX
{
public static void Main()
{
Console.WriteLine(MyNamespace.Test.PrivateMethod(2));
}
}";
//SyntaxTree targetTree = SyntaxFactory.ParseSyntaxTree(expressionString);
SyntaxTree targetTree = CSharpSyntaxTree.ParseText(expressionString);
Console.WriteLine("Preparing metadata references");
Assembly[] assemblys = new Assembly[4];
assemblys[0] = typeof(MyNamespace.Test).Assembly;
assemblys[1] = typeof(Console).Assembly;
assemblys[2] = typeof(object).Assembly;
assemblys[3] = Assembly.LoadFile(Path.Combine(Directory.GetCurrentDirectory(), "System.Runtime.dll"));
MetadataReference[] metadataReferences = MetadataFromAssembly(assemblys);
Console.WriteLine("Preparing default namespaces");
IEnumerable<string> DefaultNamespaces = new[] {"System", "System.Runtime"};
Console.WriteLine("Preparing compilation options");
CSharpCompilationOptions ReleaseDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release);
CSharpCompilationOptions cOptions = ReleaseDll.WithUsings(DefaultNamespaces);
//.WithMetadataImportOptions(MetadataImportOptions.All);
Console.WriteLine("Getting compilation");
CSharpCompilation compilation = CSharpCompilation.Create("Output.dll", new SyntaxTree[] {targetTree}, metadataReferences, cOptions);
Console.WriteLine("Emitting compilation");
using (var dll = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
var emitRes = compilation.Emit(dll);
if (!emitRes.Success)
{
Console.WriteLine("Emited unsuccessfully!");
foreach (var d in emitRes.Diagnostics)
Console.WriteLine(d.ToString());
return;
}
}
}
public unsafe MetadataReference[] MetadataFromAssembly(Assembly[] assemblys)
{
MetadataReference[] result = new MetadataReference[assemblys.Length];
byte *b;
int length;
for (int i = 0; i < assemblys.Length; i++)
{
if (assemblys[i].TryGetRawMetadata(out b, out length))
{
var moduleMetadata = ModuleMetadata.CreateFromMetadata((IntPtr) b, length);
var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata);
result[i] = assemblyMetadata.GetReference();
}
else
{
return null;
}
}
return result;
}
And got following error:
(8,44): error CS0117: 'Test' does not contain a definition for 'privateMember'
I've made 'WithMetadataImportOptions' and 'MetadataImportOptions' public inside Roslyn and uncomennted line
//.WithMetadataImportOptions(MetadataImportOptions.All);
And then got following error:
(8,44): error CS0122: 'Test.privateMember' is inaccessible due to its protection level
So may be it could be done using some Roslyn API?
P.S.
I know, that I can get non-public fields symbols using System.Reflection, but how do I compile the expression then?
If a member is private, you can't access it with normal code in another class. Nothing to do with Roslyn in particular.
If you really do actually need to access a private member in a different class, and you fully understand why it may not be a good idea, the code that accesses it must do so using reflection.

How to get unresolved symbols in Roslyn?

I am building a scripting engine in C# using Roslyn, and I would like to compile a piece of code from the user. In the scripting UI, the user can add references to other C# dlls that I don't know about.
In the user's code, I would like to find the symbols that are resolved looking into the known references, and the symbols that are not resolved.
For instance, I have a a dll that contains this class:
public class A {
public static double Stuff { get; }
}
And the users adds this dll as a reference for his script.
Then in his script, the user writes:
var x = A.Stuff * MyVariable;
return x;
I want to use Roslyn to compile this, and tell me that x and A.Stuff are known symbols and that MyVariable is not, so that I can infer from the code that MyVariable is a user input.
Right now I am doing this:
var syntaxTree = CSharpSyntaxTree.ParseText(usercode,
new CSharpParseOptions(LanguageVersion.Default, DocumentationMode.None, SourceCodeKind.Script));
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var userlib = MetadataReference.CreateFromFile(userlibPath);
var compilation = CSharpCompilation.Create("MyCompilation",
syntaxTrees: new[] { syntaxTree }, references: new[] { mscorlib, userlib });
var model = compilation.GetSemanticModel(syntaxTree);
But I don't know how to use the information from the semantic model. This is not very well documented anywhere...
You can try get variable declaration and check it:
var decl = model.GetSymbolInfo(identifier)
.Symbol
?.DeclaringSyntaxReferences
.FirstOrDefault();

c# create an instance of an object from string

I have a string variable contain:
string classCode = "public class Person { public string Name{get;set;} }";
How can I create an instance of an object from the classCode ?
like
object obj = CreateAnInstanceAnObject(classCode);
You'll need to use CodeDom to compile an in-memory assembly, and then use reflection to create the type.
Here's a sample article on MSDN that walks through the process of code generation.
Once you've compiled the code, you can use Activator.CreateInstance to create an instance of it.
Building on the answers from above, here is a working demo to generate, compile and instantiate a class from an in-memory assembly:
namespace DynamicCompilation
{
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
internal static class Program
{
private static void Main()
{
var ccu = new CodeCompileUnit();
var cns = new CodeNamespace("Aesop.Demo");
cns.Imports.Add(new CodeNamespaceImport("System"));
var ctd = new CodeTypeDeclaration("Test")
{
TypeAttributes = TypeAttributes.Public
};
var ctre = new CodeTypeReferenceExpression("Console");
var cmie = new CodeMethodInvokeExpression(ctre, "WriteLine", new CodePrimitiveExpression("Hello World!"));
var cmm = new CodeMemberMethod
{
Name = "Hello",
Attributes = MemberAttributes.Public
};
cmm.Statements.Add(cmie);
ctd.Members.Add(cmm);
cns.Types.Add(ctd);
ccu.Namespaces.Add(cns);
var provider = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
CompilerOptions = "/target:library /optimize",
GenerateExecutable = false,
GenerateInMemory = true
};
////parameters.ReferencedAssemblies.Add("System.dll");
var results = provider.CompileAssemblyFromDom(parameters, ccu);
if (results.Errors.Count == 0)
{
var t = results.CompiledAssembly.GetType("Aesop.Demo.Test");
var inst = results.CompiledAssembly.CreateInstance("Aesop.Demo.Test");
t.InvokeMember("Hello", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, inst, null);
}
Console.ReadLine();
}
}
}
Simple put you cannot do this in one line as you are attempting. It is possible to create an instance of an existing class via it's name and one of the overloads of Activator.CreateInstance.
What you are trying to achieve here though is quite different. You are attempting to both 1) define a new class type and 2) create an instance of it. Defining new metadata in the running process dynamically is very difficult to achieve with static languages like C#. It requires a significant amount of work that can't easily be put into a StackOverflow answer.
The following project should guide you in what your trying to accomplish:
RunTime Code Compilation
However, if you are attempting to write code at runtime, you may want to rethink your architecture. You may be creating more of a headache for yourself than you need to be.
What are you trying to accomplish by creating this object?

Categories