I made a C# compilation program with Roslyn. However, it takes about 1 second to compile the entire project.
I'm trying to optimize the time and here's my try:
// I'm currently recycling the compilation object.
if (compiler == null)
compiler = CreateCompiler();
/* ... */
for (var tree in syntaxTrees) {
SyntaxTree oldTree;
if (PreviouslyAddedAndHasChanges(tree)) {
compiler = compiler.ReplaceSyntaxTree(oldTree, tree);
}
else if (NewlyAdded(tree)) {
compiler = compiler.AddSyntaxTree(tree);
}
}
compiler.Emit(...);
But it doesn't work. Output assembly won't be changed.
I also take a look EmitDifference method, but it does not work with .dll output.
Here's my question:
Is it safe to re-use CSharpCompilation object just like my first line of the code?
Does Roslyn caching previous compilation data and should I invalidate it?
Self answer:
I fixed it now. The problem was not related with Roslyn.
The two assemblies have exactly the same name, so Assembly.Load() does not work at all.
Here's my solution to fix it.
compiler = compiler.WithAssemblyName("some_name" + (new System.Random()).Next(10000000));
And, the answers are:
Yes
I'm not sure, but it seems that they re-compile it if there is any changes.
Related
I have a line of script which gets a class refrence from third party script(NGUI package):
UIEventListener.Get(this.gameObject).onClick += expiryDateSettingsUIController.ActiveDeactiveUICaller;
The UIEventListener class belongs to NGUI package. My problem is that I don't want to show any error if that particular class doesn't exists. How can I do that?. If the class is not available then it is throwing compiler error and won't allowing me to build the exe.
You can use GetType() to reflect the class you are looking for:
Type mType = Type.GetType("UIEventListener");
if(mType != null)
{
UIEventListener.Get(this.gameObject).onClick += expiryDateSettingsUIController.ActiveDeactiveUICaller;
}
C# is a compiled language, so before you can run any code, it needs to be error-free. An absent class is a very serious compilation error. There is no way (in Unity or otherwise) that you can achieve the behavior you're asking for.
However, you can use conditional compilation. Conditional compilation, as the name suggests, is when you strip pieces of code out during compilation, so those pieces are never compiled and no compilation errors will be encountered for those. Wrap your code in #if/#endif like below:
#if WITH_NGUI
UIEventListener.Get(this.gameObject).onClick += expiryDateSettingsUIController.ActiveDeactiveUICaller;
#endif
Then, to let this code compile, you will need to define the WITH_NGUI conditional compilation symbol. In Unity, you can go to Player Settings, and there will be a box named Scripting Define Symbols where you can type in as many symbols as you like. Type WITH_NGUI into that box, and the code will compile. When you don't have NGUI available, simply remove the symbol and the code won't compile any more.
This isn't a nice thing to do though. You've been warned.
EDIT: You could also use reflection to access classes which may be absent from your build, but that's way more trouble than it's worth.
I am working on a code analyser using Roslyn and my current task is to find all internal methods which are unused in the assembly.
I start with a MethodDeclarationSyntax and get the symbol from that. I then use the FindCallersAsync method in SymbolFinder, but it returns an empty collection even when I am making a call to the method in question somewhere in the assembly. See the code below.
protected override void Analyze(SyntaxNodeAnalysisContext context)
{
NodeToAnalyze = context.Node;
var methodDeclaration = NodeToAnalyze as MethodDeclarationSyntax;
if (methodDeclaration == null)
return;
var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration) as ISymbol;
if (methodSymbol.DeclaredAccessibility != Accessibility.Internal)
return;
var solutionPath = GetSolutionPath();
var msWorkspace = MSBuildWorkspace.Create();
var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result;
var callers = SymbolFinder.FindCallersAsync(symbol, solution).Result; // Returns empty collection.
...
}
I have seen similar code here, but in that example the method symbol is obtained using GetSymbolInfo on an InvocationExpressionSyntax:
//Get the syntax node for the first invocation to M()
var methodInvocation = doc.GetSyntaxRootAsync().Result.DescendantNodes().OfType<InvocationExpressionSyntax>().First();
var methodSymbol = model.GetSymbolInfo(methodInvocation).Symbol;
//Finds all references to M()
var referencesToM = SymbolFinder.FindReferencesAsync(methodSymbol, doc.Project.Solution).Result;
However, in my case, I need to find the invocations (if any) from a declaration. If I do get the invocation first and pass in the symbol from GetSymbolInfo the calls to the method are returned correctly - so the issue seems to be with the symbol parameter and not solution.
Since I am trying to get the underlying symbol of a declaration, I cannot use GetSymbolInfo, but use GetDeclaredSymbol instead (as suggested here).
My understanding from this article is that the symbols returned from GetDeclaredSymbol and GetSymbolInfo should be the same. However, a simple comparison using Equals returns false.
Does anyone have any idea of what the difference is between the two symbols returned and how I can get the 'correct' one which works? Or perhaps there is a better approach entirely? All my research seems to point to FindCallersAsync, but I just can't get it to work.
My understanding from this article is that the symbols returned from GetDeclaredSymbol and GetSymbolInfo should be the same. However, a simple comparison using Equals returns false.
This is because they're not the same symbol; they are coming from entirely different compilations which might or might not be different. One is coming from the compiler that is actively compiling, one is coming from MSBuildWorkspace.
Fundamentally, using MSBuildWorkspace in an analyzer is unsupported. Completely. Don't do that. Not only would that be really slow, but it also has various correctness issues, especially if you're running your analyzer in Visual Studio. If your goal is to find unused methods anywhere in a solution, that's something we don't really support implementing as an analyzer either, since that involves cross-project analysis.
I decompiled a C# assembly using ILSpy. Opened it as a project in VC.
A small portion of the code throws errors that I don't know how to fix. Here's the code:
public static class CoroutineUtils
{
[DebuggerHidden]
public static IEnumerator WaitForRealSeconds(float time)
{
CoroutineUtils.<WaitForRealSeconds>c__Iterator2F <WaitForRealSeconds>c__Iterator2F = new CoroutineUtils.<WaitForRealSeconds>c__Iterator2F();
<WaitForRealSeconds>c__Iterator2F.time = time;
<WaitForRealSeconds>c__Iterator2F.<$>time = time;
return <WaitForRealSeconds>c__Iterator2F;
}
}
And here's the error: Unexpected character '$' (at line 8 in this case).
And if I open the .cs file in which the error appears, the compiler starts throwing a dozen more errors like Identifier expected at line 6 (right after "CoroutineUtils.")
Don't know what to do.
You can't just copy/paste decompiled code and be sure it will work. Compiler can use identifiers that are not valid in C# code but are valid in IL. It happens mostly for compiler generated code - automatic properties, anonymous types, iterators and async/await converted to state machines, etc. That's the case here.
It's really hard to say what the code is suppose to do, so it's really hard to say how to fix it.
I'm using an up-to-date .NET Reflector to disassemble an internal legacy app whose source code is almost impossible to recover. I need to find the cause of a nasty bug, and then possibly patch it. Reflector did a good job as usual in the re-creation of the project's structure, but soon I discovered that some property calls were left "expanded" to its get_() and set_() method signatures, rendering the source code impossible to compile.
At first, I thought that every get/set call had the problem. But at a closer look, several of them are OK, while others (especially OleDbCommand and Forms.Control properties) will be generated as get_() and set_().
A quick Visual Studio "Search/Replace" with regex solved these cases, but it's awkward. Is there a way to make Reflector behave correctly?
EDIT 1 - Sample problematic code below:
/* Generated by .NET Reflector 6.1.0.11 */
/* The variable selectCommand is a OleDbCommand. */
string parameterName = "#P" + Convert.ToString(num);
selectCommand.set_CommandText(selectCommand.get_CommandText() + " WHERE SIGLA = " + parameterName);
/*
Expected something like this (as ugly as it may seem):
selectCommand.CommandText = selectCommand.CommandText + " WHERE SIGLA = " + parameterName;
*/
EDIT 2 - The assembly was built in Release mode.
Where are you viewing the source code in Reflector? In the current version (6.1.0.11 at the time of this writing), disassembling a type then clicking on "Expand Methods" at the bottom yields a full class definition with code, including the correct property syntax (get { ... } and set { ... })
This problem appears with disassembling to Managed C++, right? Might want to disassemble to C# code (there is dropdown in the toolstrip) and you will get the usual properties.
So even if this question is quite old an a correct answer will never be achieved, you can now maybe give the the new tool on the block ILSpy a chance.
Maybe it will produce some better source code out of the box.
The following code throws an exception. If there is no easy answer or stuff to check, I'll try to produce something that reproduces the error (though I don't know where to upload it).
public static XMLobj Load(string FileName)
{
if (File.Exists(FileName) == false)
{
return null;
}
IRDnet.XMLobj def;
XmlSerializer xmlser = new XmlSerializer(typeof(IRDnet.XMLobj));
System.IO.Stream stream = File.OpenRead(FileName);
object o = xmlser.Deserialize(stream);
// o appears to be of correct type in the quick watch.
IRDnet.XMLobj def2 = o as IRDnet.XMLobj;
// def2 is "undefined" (as operator rejected o)
def = (IRDnet.XMLobj)o;
// Throws InvalidCastException with no InnerException.
stream.Close();
return def;
}
The strange thing is that "o" appears to be of correct type if I break just before the exception is thrown:
o {IRDnet.XMLobj} System.Object
And the object casts just fine in the quickwatch window. Values are easily inspected.
It is executed from the same project that it is part of. So, no loading contexts.
FYI: the static method is part of the XMLobj class if that's relevant.
Is there some other criteria for a successful cast that I'm not aware of? Any code that gets implicitly executed?
I've checked that reflector produces the equivalent code to make sure that nothing was lost in compiler optimization.
Any clues, people? I'm stumped. I even hoped that just writing this question would make me think twice on something completely obvious.
Chances are this is a versioning issue. That is, the deserialized XMLobj is a different version to the one you've compiled with. Check the fully qualified name of each type.
The .NET Serializer produces {Assembly}.Serializer.dll assemblies to speed up XML serialization/deserialization. Try to delete every Assembly and compile from scratch.
If the assemblies does not match exactly an InvalidCast Exception is throw.
EDIT: Look in your debugger output to see what assemblies have been loaded.
UPDATE:
string tmp = o.GetType().AssemblyQualifiedName;
string tmp2 = typeof(XMLobj).AssemblyQualifiedName;
produces:
tmp "IRDnet.XMLobj, IRDnet, Version=1.0.3600.18887, Culture=neutral, PublicKeyToken=null" string
tmp2 "IRDnet.XMLobj, IRDnet, Version=1.0.3601.27699, Culture=neutral, PublicKeyToken=null" string
So there is definitely a legitimate type mismatch. I highly appreciate this help. I was completely stuck. Now I have a lead. Now to find how the old type survived the rebuilding.
Maybe the XML file has something to say about it....
Or maybe it is the secretive Serialize-assembly somewhere that was mentioned....
I'm not stuck. I'm still digging, but I thought I'd inform of this new development. If anybody has more tips about interrogating a type about its declaration, then please chip in. Otherwise, thank you all!
ANSWER
The first thing to do if you ever get into this exception for seeming identical types, is to check that the two types are actually the same.
System.Type t1 = typeof(XMLobj);
System.Type t2 = o.GetType();
And then check them in the debugger. Take especially note of the "AssemblyQualifiedName". My two types turned out to be different versions of the same class.
Then do pay careful attention to the output window in Visual Studio as you are single stepping through. It will tell you when which assemblies are loaded. In my example, the culprit came in clear text in the debug output just as I stepped over the line
XmlSerializer xmlser = new XmlSerializer(typeof(IRDnet.XMLobj));
There, the debugger revealed that another, older, version of my assembly was loaded from a location that I wasn't aware of.
I neglected to tell the guys here that I debug using an external exe that loads my assembly. It turns out that the CLR, I assume on behalf of the XmlSerializer class constructor to resolve the "typeof", looks in the exe folder and its subfolders (which is what CLR does). And, lo and behold, deep down there there was a bastard of the original version of the IRDnet.dll file that I was fixing.
I still do not see how the two "typeof" statements produce different results: One results in the object type currently within context, whereas the other causes the CLR do go out looking for it in the execution folder. One possible theory is that the typeof statement is somehow lazily evalutated. I wouldn't know.