High Activity

News

  Analyzed 5 days ago based on code collected 5 days ago.
 
Posted about 1 year ago by DanielGrunwald
Just after the ILSpy 2.0 release, I started adding support for decompiling C# 5 async/await to ILSpy.

You can get the async-enabled ILSpy build from our build server.

The async support is not yet complete; for example decompilation ... [More] fails if the IL evaluation stack is not empty at the point of the await expression.

The decompilation logic highly depends on the patterns produced by the C# 5 compiler - it only works with code compiled with the C# compiler in the .NET 4.5 beta release, not with any previous CTPs. Also, it is likely that ILSpy will need adjustments for the final C# 5 compiler.

While testing, I found that the .NET 4.5 beta BCL was not compiled with the beta compiler - where the beta compiler uses multiple awaiter fields, the BCL code uses a single field of type object and uses arrays of length 1. This is similar to the code generated by the .NET 4.5 developer preview, so my guess is that Microsoft used some internal version in between the developer preview and the beta for compiling the .NET 4.5 beta BCL. For more information, take a look at Jon Skeet's description of the async codegen changes.
This means the ILSpy cannot decompile async methods in the .NET 4.5 beta BCL. This problem should disappear with the next .NET 4.5 release (.NET 4.5 RC?).

So how does ILSpy decompile async methods, then? Consider the compiler-generated code of the move next method:

// Async.$AwaitInLoopCondition$d__17
void IAsyncStateMachine.MoveNext()
{
    try
    {
        int num = this.$1__state;
        TaskAwaiter<bool> taskAwaiter;
        if (num == 0)
        {
            taskAwaiter = this.$u__$awaiter18;
            this.$u__$awaiter18 = default(TaskAwaiter<bool>);
            this.$1__state = -1;
            goto IL_7C;
        }
        IL_23:
        taskAwaiter = this.$4__this.SimpleBoolTaskMethod().GetAwaiter();
        if (!taskAwaiter.IsCompleted)
        {
            this.$1__state = 0;
            this.$u__$awaiter18 = taskAwaiter;
            this.$t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<bool>, Async.$AwaitInLoopCondition$d__17>(ref taskAwaiter, ref this);
            return;
        }
        IL_7C:
        bool arg_8B_0 = taskAwaiter.GetResult();
        taskAwaiter = default(TaskAwaiter<bool>);
        if (arg_8B_0)
        {
            Console.WriteLine("Body");
            goto IL_23;
        }
    }
    catch (Exception exception)
    {
        this.$1__state = -2;
        this.$t__builder.SetException(exception);
        return;
    }
    this.$1__state = -2;
    this.$t__builder.SetResult();
}

The state machine works similar to the one used by yield return; so we could reuse a lot of the code from the yield return decompiler.
Each try block begins with a state dispatcher: depending on the value of this.$1__state, the code jumps to the appropriate location. If the async method involves exception handling, there will be a separate state dispatcher at the beginning of each try block.
In this case, there are only two states: the initial state (state = -1) and the state at the await expression (state = 0). The state dispatcher consists only of the two statements "int num = this.$1__state; if (num == 0)". We rely on the fact that in the actual IL code, the state dispatcher is a contiguous sequence of IL instructions, in front of any of the method's actual code.

Note that the async/await decompiler step runs on the ILAst very early in the decompiler pipeline, immediately after the yield return transform, which is prior to any control flow analysis. We're basically still dealing with IL instructions here; but I'm explaining it in terms of C# as that is easier to read (and makes the code much shorter).

The analysis of the state dispatcher works using symbolic execution; it is described in more detail in the yield return decompiler explanation. In our example, the result of the analysis is that the beginning of the first if statement is reached for state==0, and label IL_23 is reached for all other states.

With this information, we start cleaning up the control flow of the method. We look for any 'return;' statements and analyze the instructions directly in front:

            this.$1__state = 0;
            this.$u__$awaiter18 = taskAwaiter;
            this.$t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<bool>, Async.$AwaitInLoopCondition$d__17>(ref taskAwaiter, ref this);
            return;

We then replace this piece code with an instruction that represents the AwaitUnsafeOnCompleted call (represented as "await ref taskAwaiter;" in the following code), followed by a goto to the label for the target state (using the information gained from the symbolic execution). We also remove the boilerplate associated with the $t__builder and the state dispatcher. For demonstration purposes, I'll skip the remaining steps of the async/await decompiler and resume the pipeline to decompile the ILAst to C#, producing the following code:

public async void AwaitInLoopCondition()
{
    while (true)
    {
        TaskAwaiter<bool> taskAwaiter = this.$4__this.SimpleBoolTaskMethod().GetAwaiter();
        if (!taskAwaiter.IsCompleted)
        {
            await ref taskAwaiter;
            taskAwaiter = this.$u__$awaiter18;
            this.$u__$awaiter18 = default(TaskAwaiter<bool>);
            this.$1__state = -1;
        }
        bool arg_8B_0 = taskAwaiter.GetResult();
        taskAwaiter = default(TaskAwaiter<bool>);
        if (!arg_8B_0)
        {
            break;
        }
        Console.WriteLine("Body");
    }
}

As you can see, this transformation has simplified the control flow of the method dramatically.

We now just perform some finishing touches on the method:

Access to the state machine fields is replaced with local variable access, e.g. "this.$4__this" becomes "this".
We detect the "GetAwaiter() / if (!taskAwaiter.IsCompleted) / GetResult() / clear awaiter" pattern and replace it with a simple await expression

Mind that all of this isn't done on the C# representation, but in an early stage of the ILAst pipeline. After some simplifications (variable inlining, copy propagation), the resulting ILAst looks like this:

br(IL_23)
IL_16:
call(Console::WriteLine, ldstr("Body"))
IL_23:
brtrue(IL_16, await(callvirt(Async::SimpleBoolTaskMethod, ldloc(this))))
ret()

Apart from the 'await' opcode, this is exactly the same as the while-loop would look in a non-async method. The remainder of the decompiler pipeline will detect the loop and translate it to the C# code you've seen in the introductory screenshot. [Less]
Posted about 1 year ago by ChristophWille
Today, we released ILSpy 2.0 final to the Web:

ILSpy_Master_2.0.0.1595_RTW_Binaries.zip
ILSpy_Master_2.0.0.1595_RTW_Source.zip

Daniel's Beta feature post still applies to the release version:

Assembly ... [More] Lists
Support for decompiling Expression trees
Support for lifted operatores on nullables
Decompile to Visual Basic
Search for multiple strings separated by space (searching for "Assembly manager" in ILSpy.exe would find AssemblyListManager)
Clicking on a local variable will highlight all other occurrences of that variable
Ctrl+F can be used to search within the decompiled code view

However, one thing changed for the binaries distribution: we do not include the debugger addin by default (it is part of the source download). The reason is that it is not stable enough - we´re improving the debugger in SharpDevelop and don´t have the resources to port those changes over to ILSpy just yet. [Less]
Posted about 1 year ago by siegi44
As Chris already announced in a previous blog post, SharpDevelop 4.2 and later will support targeting .NET 4.5. One of the interesting new features in .NET 4.5/C# 5 is async. If you have already tried debugging an async method, you will have noticed ... [More] , that SharpDevelop's debugger is unable to evaluate any local variables.

In short this is because async is - similar to iterator blocks (yield return) - implemented as state machine in a compiler-generated struct. So the debugger has to find the correct location (i.e. compiler-generated code) where all the local variable's values are stored.

In the most recent nightly builds of SharpDevelop 4.2 Beta 2 (starting with build 4.2.0.8661), we've applied the necessary plumbing to make it work. But see for yourself: [Less]
Posted about 1 year ago by siegi44
With the upcoming release of SharpDevelop 4.2, you get support for ASP.NET MVC projects based on IIS and IIS Express. We also want to give you debugging support. However this required some changes in our project system, because web development ... [More] usually involves a web server and the IDE attaches its debugger to the web server when the project is run. This is fundamentally different from normal projects.

Another point is that projects consist of different "project types". For example a WPF project is usually C# + WPF or VB + WPF. An ASP.NET MVC project even consists of three parts: C#, web and MVC.

The Solution

Visual Studio solves this by inserting different project type GUIDs into the project file. Depending on the GUIDs it opens the project file with a different handler. While this approach is a good idea, its implementation in Visual Studio requires the project file to be read two times. First it is opened and scanned for the project type GUIDs and then opened again with an XML parser and finally loaded completely. (Source: [1])

So each project type can have various sub-types defined by GUIDs. Scala implements such type compositions through mixins. In fact we would need something like this (pseudo syntax):

var mvcProject = new CSharpProject() with WebSupport(), MVCSupport();

Of course this is not possible in C#. So we borrowed this concept and implemented it through project behavior chains, as described below:

In SharpDevelop we first load the project file into an IProject container. Which container to use is defined by the file extension. So there is a CSharpProject (.csproj), VBProject (.vbproj) or FSharpProject (.fsproj) and so on. After the container is initialized and the file has been loaded, we look for the project type GUIDs and then load all the project behaviors that apply to the different GUIDs.

A project behavior is a set of virtual methods that can be overridden to alter certain behavior of the project (for example Start, which starts the project with or without debugger). Project behaviors are chained together in the order they appear in the AddIn Tree. If a project behavior does not want to handle a certain call, it is passed onto the next project behavior. At the end of the chain there is the default project behavior which implements the defaults for each project type.

This change allows AddIns to implement different handlers for different projects without modifying the whole project system and/or the debugger.

 

[1] http://connect.microsoft.com/VisualStudio/feedback/details/481981/adding-a-file-named-projecttypeguids-cs-to-a-library-project-causes-following-error-the-project-type-is-not-supported-by-this-installation [Less]
Posted about 1 year ago by DanielGrunwald
SharpDevelop has had multi-targeting support for a long time - for example, SharpDevelop 2.0 supported targeting .NET 1.0, 1.1 and 2.0. Our original multi-targeting implementation would not only change the target framework, but also use the matching ... [More] C# compiler version*.

When Visual Studio 2008 and MSBuild 3.5 came along and introduced official multi-targeting support, we separated the 'target framework' and 'compiler version' settings. The 'target framework' setting uses the <TargetFrameworkVersion> MSBuild property, which is the official multi-targeting support as in Visual Studio 2008. The 'compiler version' setting determines the MSBuild ToolsVersion, which controls the version of the C# compiler to use - Visual Studio does not have this feature.

I'll call the latter feature MSBuild Multi-Targeting, as this allows us to pick the MSBuild version to use, and thus enables SharpDevelop to open and edit VS 2005 or 2008 projects without having to upgrade them to the VS 2010 project format.

Unfortunately, life isn't as simple as that. It turns out that MSBuild 4.0 is unable to compile projects with a ToolsVersion lower than 4.0 if the Windows SDK 7.1 is not installed. To allow users to use SharpDevelop without downloading the Windows SDK, we implemented a simple fix: we use MSBuild 3.5 to compile projects with a ToolsVersion of 2.0 or 3.5. This is why SharpDevelop ships with both
"ICSharp­Code.Sharp­Develop.Build­Worker40.exe" and
"ICSharp­Code.Sharp­Develop.Build­Worker35.exe".

Now what happens if SharpDevelop is run on a machine without .NET 3.5? If the framework specified by the 'ToolsVersion' is missing, SharpDevelop crashed with an MSBuild error when opening the project. There were also crashes when creating/upgrading projects to missing ToolsVersions. Moreover, in the rare scenario where .NET 2.0 and .NET 4.0 are installed, but .NET 3.5 is missing, SharpDevelop was able to open the project but the build worker would crash when trying to compile.

For this reason, the SharpDevelop 4.0 and 4.1 setups require both  .NET 3.5 and .NET 4.0 to be installed. This wasn't an issue when we made that decision - .NET 3.5 is likely to be already installed on most machines. However, Windows 8 will change that - .NET 4.5 is installed by default, but .NET 3.5 is missing. So we added the necessary error handling to SharpDevelop 4.2. The SharpDevelop 4.2 setup no longer requires .NET 3.5 - you'll need it only when targeting .NET 2.0/3.0 or 3.5.

Another issue is that .NET 4.0 does not ship with the Reference Assemblies - you need to install the Windows SDK to get those. This causes MSBuild to reference the assemblies in the GAC instead, which might be a later version (due to installed service packs or in-place upgrades like .NET 4.5), and also emit massive amounts of warnings (one warning per reference). Moreover, it caused the 'Copy Local' flag to default to true for references to .NET assemblies, causing System.dll etc. to be copied into the output directory.

At the time, the reference assemblies were only available as part of Visual Studio 2010 - the free Windows SDK 7.1 was released later. So it was a high priority for us to work around this problem. For this reason, SharpDevelop injects a custom MSBuild .targets file into the project being built: SharpDevelop.TargetingPack.targets. This file runs a simple custom MSBuild task that detects references to default .NET assemblies and sets the 'Copy Local' flag to false. (we also inject several other custom .targets files; for example for running FxCop or StyleCop as part of a build)

We used the Microsoft.Build.Utitilies.dll when implementing this custom task. However, that library ships only with .NET 2.0, not with .NET 4.0, so we had to switch to Microsoft.Build.Utitilies.v4.dll to get the C# 4.0 build working without .NET 2.0. This should not be a problem as the copy local workaround is only included when targeting .NET 4.0 or higher, so we won't try to load it the 3.5 build worker process.

 

To summarize, the SharpDevelop 4.2 setup requires:

Windows XP SP2 or higher
.NET 4.0 Full (.NET 4.5 Full will also work)
VC++ 2008 runtime (part of .NET 3.5 so most people have it already)
In the minimal configuration, you can only compile for .NET 4.0 using MSBuild 4.0.

Additionally:

If .NET 4.5 is installed, the C# 5 compiler will replace the C# 4 compiler; and .NET 4.5 will appear as an additional target framework.

If .NET 3.5 SP1 is installed, you will be able to use .NET 2.0/3.0/3.5 as target framework, and C# 2 and C# 3 as compiler versions.
Installing the Windows SDK 7.1 is highly recommended (provides reference assemblies and documentation for code completion).

Some SharpDevelop features might require installation of additional tools such as FxCop, StyleCop, F#, TortoiseSVN, SHFB.

* Everything said about the C# compiler in this post also applies to the VB compiler. [Less]
Posted about 1 year ago by ChristophWille
We have waited till the Beta phase of .NET Framework 4.5 to finally decide on our platform story - what will be supported and what will be required.

SharpDevelop 4.x

It will continue to require .NET 4.0 as its runtime, but it will ... [More] also run on .NET 4.5 (if you upgraded your machine). It supports (as compilation targets) 2.0 to 4.5 - if those frameworks are installed on your machine.

We are currently wrapping up feature development for 4.2, and we have plans for a further feature release 4.3. After that, the current plans are for servicing releases that update dependencies and/or fix bugs. Given those plans, expect 4.x releases for at least a year to come.

SharpDevelop 5 "Zimnitz"

This has been in development for some time already, and you have seen "offsprings" like ILSpy or Code Quality Analysis in SharpDevelop 4.2 to "prove" that our new NRefactory (*) is up to the job. Given that all IDE services need to be adapted to the new infrastructure, we will also take advantage of features in .NET 4.5, thus upping the requirements of SharpDevelop.

However, this means for a considerable part of our current user base to start planning for the future - because .NET 4.5 will likely not support XP (maybe not even Vista). Betas of SD5 will start showing up later this year.

Although the requirements change, the support of frameworks stays the same. (you will only have to install a newer OS on your development machine, deployment is unaffected)

* NRefactory 5 is developed together with the MonoDevelop team, and will support features such as semantic highlighting or more (sophisticated) refactorings. [Less]
Posted about 1 year ago by ChristophWille
With support for .NET 4.5, and yes, it does run on Windows 8 Consumer Preview too:
Posted over 1 year ago by DanielGrunwald
After a long pause, we have finally released the first Beta of ILSpy 2.0.

Download:

ILSpy 2.0.0.1564 Beta Binaries.zip
ILSpy 2.0.0.1564 Beta Source Code.zip

New features compared with version 1.0:

Assembly ... [More] Lists
Support for decompiling Expression trees
Support for lifted operatores on nullables
Integrated Debugger
Decompile to Visual Basic
Search for multiple strings separated by space (searching for "Assembly manager" in ILSpy.exe would find AssemblyListManager)
Clicking on a local variable will highlight all other occurrences of that variable
Ctrl+F can be used to search within the decompiled code view [Less]
Posted over 1 year ago by siegi44
In SharpDevelop 4.2 Beta we added a new feature which was partially developed by Tomas Linhart during GSoC 2010. Because it was never finished it took us some time to complete it after GSoC. One of the biggest changes after GSoC was the rewrite of ... [More] the core engine. The engine now uses the new NRefactory 5 Type System in combination with Mono.Cecil.

It is nearly done now, only little changes to the UI will be made until the final release of SharpDevelop 4.2.

To launch the addin, simply select Analysis > Analyze Code Quality from the main menu. First you have to select a set of assemblies from the file system. After the analyzer completes the matrix above will be shown. The colours are  quite simple:

Green: left uses top
Blue: left is used by top
Turquoise: left uses and is used by top

The numbers in the cells tell you how many dependencies there are from left to top or vice versa.

For example this code:

string M(string a) { return a.ToLower(); }

It has three usages of the type System.String and one usage of the method System.String.ToLower.

But in our matrix we show only one dependency on System.String, otherwise the numbers would be too noisy. This shows that there is one class from mscorlib involved in the method M.

This is only a preview and I hope it is useful to you. If you have any suggestions or find bugs, please feel free to post a comment below or on the forums.

  [Less]
Posted over 1 year ago by MattWard
SharpDevelop 4.2 now includes support for JavaScript code
folding and code regions.

When you open a JavaScript (.js) file into the text editor any
functions that are defined can be folded.

The text editor also ... [More] supports code regions.

//#region Canvas Test

tests['canvas'] = function() {
var elem = document.createElement( 'canvas' );
return !!(elem.getContext && elem.getContext('2d'));
};

//#endregion
These regions can be folded as shown below. [Less]
 

 
 

Creative Commons License Copyright © 2013 Black Duck Software, Inc. and its contributors, Some Rights Reserved. Unless otherwise marked, this work is licensed under a Creative Commons Attribution 3.0 Unported License . Ohloh ® and the Ohloh logo are trademarks of Black Duck Software, Inc. in the United States and/or other jurisdictions. All other trademarks are the property of their respective holders.