When I'm getting IInstanceReferenceExpression operation for type Instance() => this; instance kind is Explicit, but I expect that kind would be This. Am I missing something or this is a bug in Roslyn?
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Semantics;
using System;
using System.Linq;
internal static class so39495857
{
private static void Main()
{
var tree = CSharpSyntaxTree.ParseText(#"
class c
{
c Instance() => this;
static void Main() {}
}
");
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create(null, new[] { tree }, new[] { mscorlib });
var model = compilation.GetSemanticModel(tree);
var node = tree.GetRoot().DescendantNodes().OfType<ArrowExpressionClauseSyntax>().First();
var operation = model.GetOperation(node);
var block = (IBlockStatement)operation;
var #return = (IReturnStatement)block.Statements.First();
var #this = (IInstanceReferenceExpression)#return.ReturnedValue;
Console.WriteLine(#this.InstanceReferenceKind);
}
}
Project on github - https://github.com/isanych/so-39495857
That's what Explicit means.
ThisClass is only for VB's MyClass keyword.
Related
Using the Roslyn API with Visual Studio 2015, can I convert an object instance to source code? Can I create an extension method like ".ToSourceCode()" as shown below?
class Foo { }
class Program
{
static string classSourceCode = "class Foo { }";
static void Main()
{
var instance = new Foo();
var instanceSourceCode = instance.GetType().ToSourceCode();
System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
}
}
No. However, ILSpy can.
Based on the comments on the question and what I understand about Roslyn, decompilation is not supported. However, thanks to #Bradley's ILSpy tip, there is a solution:
Download the ILSpy binaries from http://ilspy.net/
Reference the following assemblies: ICSharpCode.Decompiler.dll, ILSpy.exe, Mono.Cecil.dll, ILSpy.BamlDecompiler.Plugin.dll
Implement the ".ToSourceCode()" extension method as shown below:
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy;
using Mono.Cecil;
class Foo { }
class Program
{
static string classSourceCode = "using System; internal class Foo { } ";
static void Main()
{
var instance = new Foo();
var instanceSourceCode = instance.GetType().ToSourceCode();
System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
}
}
static class TypeExtensions
{
public static string ToSourceCode(this Type source)
{
var assembly = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
var type = assembly.MainModule.Types.FirstOrDefault(t => t.FullName == source.FullName);
if (type == null) return string.Empty;
var plainTextOutput = new PlainTextOutput();
var decompiler = new CSharpLanguage();
decompiler.DecompileType(type, plainTextOutput, new DecompilationOptions());
return Regex.Replace(Regex.Replace(plainTextOutput.ToString(), #"\n|\r", " "), #"\s+", " ");
}
}
How can I get all the classes from a Roslyn compilation?
var sln = Path.Combine(path, "xxx.sln");
var workspace = MSBuildWorkspace.Create();
var solution = await workspace.OpenSolutionAsync(sln);
Project project = solution.Projects.First(x => x.Name == "bbb");
var compilation = await project.GetCompilationAsync();
This is how I visit all the classes in my solution.
class ClassVirtualizationVisitor : CSharpSyntaxRewriter
{
List<string> classes = new List<String>();
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
node = (ClassDeclarationSyntax) base.VisitClassDeclaration(node);
string className = node.Identifier.ValueText;
classes.Add(className); // save your visited classes
return node;
}
}
Now use the visited classes:
var classVisitor = new ClassVirtualizationVisitor();
classVisitor.Visit(semanticModel.SyntaxTree.GetRoot());
var classes = classVisitor.classes; // list of classes in your solution
This now is my working Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using System.Threading.Tasks;
namespace Kardex.LC3xx.CreateApiDokumentation
{
//Patch to work with VS2013
// https://support.microsoft.com/en-us/kb/2971005
class Program
{
private static void Main(string[] args)
{
Run(args).Wait();
Console.ReadLine();
}
private async static Task Run(string[] args)
{
var path = Path.GetDirectoryName(typeof (Program).Assembly.Location);
var sln = Path.Combine(path, "xxx.sln");
var workspace = MSBuildWorkspace.Create();
var solution = await workspace.OpenSolutionAsync(sln);
Project project = solution.Projects.First(x => x.Name == "bbbb");
var compilation = await project.GetCompilationAsync();
foreach (var #class in compilation.GlobalNamespace.GetNamespaceMembers().SelectMany(x=>x.GetMembers()))
{
Console.WriteLine(#class.Name);
Console.WriteLine(#class.ContainingNamespace.Name);
}
var classVisitor = new ClassVirtualizationVisitor();
foreach (var syntaxTree in compilation.SyntaxTrees)
{
classVisitor.Visit(syntaxTree.GetRoot());
}
var classes = classVisitor.Classes;
}
class ClassVirtualizationVisitor : CSharpSyntaxRewriter
{
public ClassVirtualizationVisitor()
{
Classes = new List<ClassDeclarationSyntax>();
}
public List<ClassDeclarationSyntax> Classes { get; set; }
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node);
Classes.Add(node); // save your visited classes
return node;
}
}
}
}
Create a SymbolVisitor that overrides VisitNamedType to process each type (which may not be a class).
Then, pass it to compilation.Assembly.Accept().
I have a promblem with run-time compiled classes. I have something like this 2 classes:
first class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Program.Bullet {
public class Class1{
private int i;
public Class1(int j){
i=j;
}
}
}
and second class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Program.Bullet;
namespace Program.Object {
public class Class2{
public Class2(){
Class1 c1 = new Class1(5);
}
}
}
This two classes I would like to compile in run-time and use them in my project. So I have function to compile it (XmlNode has data about fullPath etc):
private ModuleBuilder moduleBuilder;
private List<string> isCompiled;
private void Compile(XmlNode compileingClasses) {
foreach (XmlNode compileingClass in compileingClasses) {
string fullPath = compileingClass.Attributes["path"].InnerText;
string type = compileingClass.Attributes["name"].InnerText; ;
if (!isCompiled.Contains(type)) {
isCompiled.Add(type);
var syntaxTree = SyntaxTree.ParseFile("../../File1/File2/" + fullPath);
var comp = Compilation.Create("Test.dll"
, syntaxTrees: new[] { syntaxTree }
, references: metadataRef
, options: comilationOption
);
// Runtime compilation and check errors
var result = comp.Emit(moduleBuilder);
if (!result.Success) {
foreach (var d in result.Diagnostics) {
Console.WriteLine(d);
}
throw new XmlLoadException("Class not found " + fullPath);
}
}
}
}
Is it possible to get the reference on Class1 to Class2?
Edit: Better question
Is it possible to create MetadataReference on compiled Class1?
Something like:
string fullName = bullet.Attributes["fullName"].InnerText;
var o = moduleBuilder.GetType(fullName);
metadataRef.Add(new MetadataFileReference(o.Assembly.Location));
This throw NotSupportedException
You're trying to reference the assembly which is currently being built and I don't think Roslyn can do that.
What you can do instead is to create a single Compilation from all your classes (probably having a separate SyntaxTree for each class). If you do that, you won't need any references.
Suppose I have "".GetType() which is ExpressionStatementSyntax (or maybe InvocationExpressionSyntax) in the syntax tree and I want to turn it into ("".GetType()), that is I want to turn my ExpressionStatementSyntax node into ParenthesizedExpressionSyntax node. How would I do that?
using System;
using System.Linq;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
class Program
{
static void Main(string[] args)
{
var oldRootNode = Syntax.ParseCompilationUnit(
"class C { void M() { \"\".GetType(); } }");
var oldStatementNode = oldRootNode.DescendantNodes().OfType<ExpressionStatementSyntax>().First();
var oldExpressionNode = oldStatementNode.Expression;
var newExpressionNode = Syntax.ParenthesizedExpression(oldExpressionNode);
var newRootNode = oldRootNode.ReplaceNode(oldExpressionNode, newExpressionNode);
Console.WriteLine(oldRootNode.ToString());
Console.WriteLine(newRootNode.ToString());
}
}
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
public int xx = 1; // variable I want to access from the runtime code
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.dll");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("ConsoleApplication1.Program");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
method.Invoke(null, null);
int a = int.Parse(Console.ReadLine()); // pause console
}
static string[] GetCode()
{
return new string[]
{
#"using System;
namespace ConsoleApplication1
{
class Program
{
public static void DynamicMethod()
{
Console.WriteLine(""Hello, world!"");
}
}
}"
};
}
}
}
I would like to know if It's possible to access variable int xx from the runetime code (eg. putting xx = 2; line after "hello world". That would be awesome
Thanks :)
Yes, you can make that available, but you need to:
add a reference to the assembly that contains xx (I'm calling this assembly StaticAssembly, for convenience - so it is probably StaticAssembly.exe, the console exe that is running)
make Program in StaticAssembly a public type so that it is accessible
either make xx into a static field, or instantiate an instance of Program and pass it to the dynamic code somehow
The following works, for example (I'm using the "instantiate an instance and pass it" approach, so I've added a parameter to DynamicMethod):
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace StaticAssembly
{
public class Program
{
public int xx = 1; // variable I want to access from the runtime code
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("StaticAssembly.exe");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("GeneratedAssembly.Program");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
var p = new Program();
p.xx = 42;
method.Invoke(null, new object[] {p});
int a = int.Parse(Console.ReadLine()); // pause console
}
static string[] GetCode()
{
return new string[]
{
#"using System;
namespace GeneratedAssembly
{
class Program
{
public static void DynamicMethod(StaticAssembly.Program p)
{
Console.WriteLine(p.xx);
Console.WriteLine(""Hello, world!"");
}
}
}"
};
}
}
}