ILSpy and F#
Suddenly a new contender in the post-Reflector age -- ILSpy, emerging from the SharpDevelop stable.
So I decided to give it a whirl with the same F# code as with the simple tests I made last week with Cecil.Decompiler -- a few extension methods for Option
module Augment = | |
type Microsoft.FSharp.Core.Option<'T> with | |
static member filter (f : 'T -> bool) (x : option<'T>) = | |
match x with | |
| Some v when f(v) -> Some v | |
| _ -> None | |
static member getOrElse (fallback : 'T) (x : option<'T>) = defaultArg x fallback | |
static member select (f : 'T -> bool) (x : 'T) = | |
if f(x) then Some x | |
else None | |
static member nullable (x : 'a when 'a : null) : option<'a> = | |
if x <> null then Some x | |
else None |
The first thing I notice is that I can't copy and paste code from the decompilation screen -- I have to save it, and which point I get a button to open Explorer in the same directory as I saved. The C# code generated looks sane
public static FSharpOption<T> Option`1.select.Static(FSharpFunc<T, bool> f, T x) | |
{ | |
if (f.Invoke(x)) | |
{ | |
} | |
else | |
{ | |
goto Block_3; | |
} | |
return FSharpOption<T>.Some(x); | |
Block_3: | |
return null; | |
} |
Unlike Cecil, this gets the F# branching right in representing the highlighted section
IL_0000: nop | |
IL_0001: ldarg.0 | |
IL_0002: ldarg.1 | |
IL_0003: callvirt !1 Microsoft.FSharp.Core.FSharpFunc`2<T,System.Boolean>::Invoke(!0) | |
IL_0008: brfalse.s IL_000c | |
IL_000a: br.s IL_000e | |
IL_000c: br.s IL_0015 | |
IL_000e: ldarg.1 | |
IL_000f: call Microsoft.FSharp.Core.FSharpOption`1<!0> Microsoft.FSharp.Core.FSharpOption`1<T>::Some(!0) | |
IL_0014: ret | |
IL_0015: ldnull | |
IL_0016: ret |
It also handles compiler generated temporaries just fine -- albeit with the same 'C'-style if
public static FSharpOption<T> Option`1.filter.Static(FSharpFunc<T, bool> f, FSharpOption<T> x) | |
{ | |
FSharpOption<T> fSharpOption = x; | |
if (fSharpOption) | |
{ | |
FSharpOption<T> fSharpOption2 = fSharpOption; | |
T t = fSharpOption2.Value; | |
if (f.Invoke(t)) | |
{ | |
goto Block_4; | |
} | |
} | |
return null; | |
Block_4: | |
return FSharpOption<T>.Some(fSharpOption2.Value); | |
} |
And ૼ bonus ૼ while Cecil choked on this C# code
private static void DumpAssembly(string path) | |
{ | |
var assembly = AssemblyDefinition.ReadAssembly(path); | |
var pdbpath = Path.ChangeExtension (path, ".pdb"); | |
var provider = new PdbReaderProvider(); | |
var reader = provider.GetSymbolReader(assembly.MainModule, pdbpath); | |
assembly.MainModule.ReadSymbols(reader); | |
assembly.MainModule.Types.SelectMany(x => x.Methods).ToList().ForEach(Decompile); | |
} |
ILSpy comes up with
private static void DumpAssembly(string path) | |
{ | |
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(path); | |
string text = Path.ChangeExtension(path, ".pdb"); | |
ISymbolReader symbolReader = new PdbReaderProvider().GetSymbolReader(assemblyDefinition.MainModule, text); | |
assemblyDefinition.MainModule.ReadSymbols(symbolReader); | |
var arg_5F_0 = assemblyDefinition.MainModule.Types; | |
if (!Program.CS$<>9__CachedAnonymousMethodDelegate1) | |
{ | |
Program.CS$<>9__CachedAnonymousMethodDelegate1 = delegate(TypeDefinition x) | |
{ | |
IEnumerable<MethodDefinition> enumerable = x.Methods; | |
return enumerable; | |
} | |
; | |
} | |
Enumerable.ToList<MethodDefinition>(Enumerable.SelectMany<TypeDefinition, MethodDefinition>(arg_5F_0, Program.CS$<>9__CachedAnonymousMethodDelegate1)).ForEach(new Action<MethodDefinition>(Program.Decompile)); | |
} |
which leaks some of the internal plumbing -- delegate caching; how extension methods are actually handled -- but does at least deliver.
No comments :
Post a Comment