Saturday, November 28, 2009

Working on F# with NDepend

Following up from the earlier post here, looking more in depth at some of the results out of NDepend for my own little code quality project, and in addition to the results noted there

First out of the gate, for sanity's sake, when working with F#, it would make sense to add !HasAttribute OPTIONAL:System.Runtime.CompilerServices.CompilerGeneratedAttribute onto every rule. That way, you are not left to contemplate how and why the generated CompareTo methods on a simple record type

type internal Context = {
Line : int;
Column : int;
EndLine : int;
EndColumn : int }
view raw gistfile1.fs hosted with ❤ by GitHub
can have an IL Cyclomatic complexity of 43.

A similar blanket exclusion should be applied to types whose names contain the sigil @ marking them as generated function objects. Unfortunately, compiler inserted write-once local variables do not have any such distinctive sigils by which they can be filtered, just unexciting normal names like str, str2.

It would be nice if the HasAttribute condition also allowed you to filter on properties of the attributes like !HasAttribute OPTIONAL:Microsoft.FSharp.Core.CompilationMappingAttribute WITH (SourceConstructFlags & 31 == 1) OR (SourceConstructFlags & 31 == 2) -- to filter record or sum types, which are rich in generated structure, where appropriate. For example, though the sum type

// After the Haskell type
type internal Either<'a, 'b> =
| Left of 'a
| Right of 'b
view raw gistfile1.fs hosted with ❤ by GitHub

may be implemented into the CLR by making Either<'a, 'b> an abstract type with concrete inner types Either<'a, 'b>+Left and Either<'a, 'b>+Right, the headline Either<'a, 'b> type is not an abstract base class in the usual sense, that user written code will be expected to extend the type.

Similarly, while it is arguably an oversight of the F# code generation that none of the inner types within a sum type are marked as sealed -- there is no good case for extending them -- it would also be nice to be able to exclude all (implicitly, generated) types that derived from (or are an inner type of) a sum or record type from analysis; as well as all the content of types attributed with [CompilationMapping(SourceConstructFlags.NonpublicRepresentation | ...)].

Allowing names of the form |ActivePattern|_| is a trivial extension of the "names should be upper case" rule.

There is what looks like a bug in NDepend's analysis as it manages to make all the types I have derived from Microsoft.FxCop.Sdk.BaseIntrospectionRule pass the filter

DepthOfInheritance == 1 // Must derive directly from System.Object

The containing rule for that test Classes that are candidate to be turned into Structures could probably also benefit from having AND !IsStatic added, so as to exempt functional modules.

Thursday, November 26, 2009

F# under the covers XI -- Literal expressions that aren't; attributes that don't

The problem with being unable to attribute just the getter or setter of a property is resolved at the 1.9.9.9 release (February 2010 CTP).

Consider the following simple C# property

public static FileShare get_Options()
{
return (FileShare.Delete | FileShare.Write);
}
view raw gistfile1.cs hosted with ❤ by GitHub

This compiles in debug mode to

.method public hidebysig specialname static valuetype [mscorlib]System.IO.FileShare get_Options() cil managed
{
.maxstack 1
.locals init (
[0] valuetype [mscorlib]System.IO.FileShare CS$1$0000)
L_0000: nop
L_0001: ldc.i4.6
L_0002: stloc.0
L_0003: br.s L_0005
L_0005: ldloc.0
L_0006: ret
}
view raw gistfile1.cs hosted with ❤ by GitHub

which assigns the literal result -- 6 -- of the expression to the temporary that is then returned. The analogous F# method

static member Options with get() = System.IO.FileShare.Delete ||| System.IO.FileShare.Write
// or equivalently
static member Options = System.IO.FileShare.Write ||| System.IO.FileShare.Delete
view raw gistfile1.fs hosted with ❤ by GitHub

compiles to IL which preserves the expression to execute only at run time:

.method public specialname static valuetype [mscorlib]System.IO.FileShare get_Options() cil managed
{
.maxstack 4
L_0000: nop
L_0001: ldc.i4.2
L_0002: ldc.i4.4
L_0003: or
L_0004: ret
}
view raw gistfile1.cs hosted with ❤ by GitHub

which makes static analysis of the code in the more usually analysed state a rather more complicated business.

Another complicating factor is that while this syntax

[<...>]
static member Options = System.IO.FileShare.Write ||| System.IO.FileShare.Delete
view raw gistfile1.fs hosted with ❤ by GitHub

builds, it attaches the attribute (with Method and Constructor usage only) to the property as a whole; and not the generated get_Options method, as in

[...]
public static FileShare Options
{
get
{
return (FileShare.Write | FileShare.Delete);
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

What I'd like to express, and what I've not found a way to express, is

public static FileShare Options
{
[...]
get
{
return (FileShare.Write | FileShare.Delete);
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

and MSDN remains opaque on the subject as well.

This is something I could code around, but I can't yet see a clean way of doing to allow separate attributes on the getters and the setters.

Later: The intended syntax for this option is, much as you might expect, this:

static member Options
with [<...>] get() =
System.IO.FileShare.Write ||| System.IO.FileShare.Delete
view raw gistfile1.fs hosted with ❤ by GitHub

with the necessary insets as shown (to avoid error FS0010: Incomplete structured construct at or before this point in pattern), however on filing a bug report I got confirmation that it is a known issue with the Beta 2 release (aka October 2009 CTP), that this still decorates the property and not just the getter.

Saturday, November 21, 2009

F# under the covers X -- the curious case of record types

This is unchanged in the February 2010 CTP (1.9.9.9) release.

They're coming thick and fast now...

Define a record type such as

type internal Context = {
Line : int;
Column : int;
EndLine : int;
EndColumn : int }
view raw gistfile1.fs hosted with ❤ by GitHub

The class that results looks like

[Serializable, CompilationMapping(SourceConstructFlags.RecordType)]
public sealed class Context : IStructuralEquatable, IComparable, IStructuralComparable
{
// Fields
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal int Column@;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal int EndColumn@;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal int EndLine@;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal int Line@;
// Methods
public Context(int line, int column, int endLine, int endColumn);
[CompilerGenerated]
public sealed override int CompareTo(object obj);
[CompilerGenerated]
public int CompareTo(Context obj);
[CompilerGenerated]
public sealed override int CompareTo(object obj, IComparer comp);
[CompilerGenerated]
public sealed override bool Equals(object obj);
[CompilerGenerated]
public bool Equals(Context obj);
[CompilerGenerated]
public sealed override bool Equals(object obj, IEqualityComparer comp);
[CompilerGenerated]
public sealed override int GetHashCode();
[CompilerGenerated]
public sealed override int GetHashCode(IEqualityComparer comp);
// Properties
[CompilationMapping(SourceConstructFlags.Field, 1)]
public int Column { get; }
[CompilationMapping(SourceConstructFlags.Field, 3)]
public int EndColumn { get; }
[CompilationMapping(SourceConstructFlags.Field, 2)]
public int EndLine { get; }
[CompilationMapping(SourceConstructFlags.Field, 0)]
public int Line { get; }
}
view raw gistfile1.cs hosted with ❤ by GitHub

where the source context for each method is the same -- the range containing just the type name.

Now, you wouldn't expect anything to be seriously unusual about this class in terms of its implementation, but there is.

FxCop reminds you about the IComparable should-haves that can't be enforced through interface constraints:

[Location not stored in Pdb] : warning : CA1036 : Microsoft.Design : 'Context' should define operator '!=' since it implements IComparable.
[Location not stored in Pdb] : warning : CA1036 : Microsoft.Design : 'Context' should define operator '<' since it implements IComparable.
[Location not stored in Pdb] : warning : CA1036 : Microsoft.Design : 'Context' should define operator '==' since it implements IComparable.
[Location not stored in Pdb] : warning : CA1036 : Microsoft.Design : 'Context' should define operator '>' since it implements IComparable.

but which aren't there; and Reflector's decompilation to C# is stymied by the highlighted CompareTo overloads -- the simple one is implemented as

[CompilerGenerated]
public sealed override int CompareTo(object obj)
{
return this.CompareTo((Context) obj);
}
view raw gistfile1.cs hosted with ❤ by GitHub

which fortunately doesn't give much scope for things to go wrong.

In this and the previous case, the offending instruction that Reflector balks at is a simple branch such as indicated in this example

L_0045: ldc.i4.m1
L_0046: nop
L_0047: br.s L_0050
L_0049: ldloc.s num2
L_004b: ldloc.s num3
L_004d: cgt
L_004f: nop
L_0050: stloc.2
view raw gistfile1.cs hosted with ❤ by GitHub

It's quite clear at every turn that F# is coming at the problem of code generation from a very different direction to the well explored parts of the phase space of valid IL that C# and VB.net dabble in. And clear, too, that this will present a significant challenge to all writers of tools to work with the language -- it takes us well out of our old comfort zone.

F# under the covers IX -- the case of the missing coverage

As I'm driving my little set of FxCop rules and associated helpers to a usable state, I'm looking in more detail at how the code coverage in the automated system/integration test is going, trying to drive that towards 100%. And as usual, the F# code generation is throwing up some interesting results.

Take this method (lightly refactored from last time) which starts at line 167 in the actual source:

member private self.ProcessMethod fn doc =
let xml = FindXmlForMethod (FindSignatureForMethod fn) doc
let uncovered = EstimateUncovered fn xml
fn >+?? xml
>?> CoverageExemptionRule.ExcludeAngleBrackets
>?> SkipFuns
>?> CoverageExemptionRule.CheckCompilerGenerated
>?> CoverageExemptionRule.CheckCompilationMapping
>?> self.CheckStatic
>?> (self.CheckExemption uncovered)
>?> (self.Fallback uncovered doc) |> ignore
view raw gistfile1.fs hosted with ❤ by GitHub

Although my tests give a match for every case in the pattern -- and nCover shows a visit for every line from 168 to 176 (2 to 9), and the expression body of each branch of the if/else, it also claims that a code point that spans from from line 168 col 5 to line 177 col 24 (everything from if to then inclusive) is not visited.

Unfortunately, the IL generated for this method is not back-compilable to C# in Reflector (yes, another problem report submitted), so I can't use that to analyse what is going on in this particular case : I shall have to inject some diagnostic code into the rule itself to see what FxCop thinks is going on as a statement which spans those lines.

But this isn't the only mysterious bit of uncoverage I've found in F# code -- in the previous state of the method (as showcased here), it was the identifier result in let result = match… that got the uncoverage, despite the named value being used as the final expression in the method.

In most cases, it is possible to refactor away such temporaries, and clear the spurious uncoverages that they give rise to; but, as this example shows, it is not always possible.

Later

By recursively dumping Statements in FxCop for this method (which inserted 2 lines in a routine above this one, thus displacing the line numbers), I get a lot of multiple hits on statements (same type, same source context range), but the only ones with the appropriate source context are

Block -> from 170 : 5 to 179 : 24
Nop -> from 170 : 5 to 179 : 24
Nop -> from 170 : 5 to 179 : 24
AssignmentStatement -> from 170 : 9 to 170 : 22
AssignmentStatement -> from 170 : 9 to 170 : 22
Branch -> from 170 : 9 to 170 : 22
Block -> from 170 : 9 to 170 : 22
Branch -> from 170 : 9 to 170 : 22
Block -> from 170 : 9 to 170 : 22
Branch -> from 170 : 9 to 170 : 22
Block -> from 171 : 27 to 171 : 35
ExpressionStatement -> from 171 : 27 to 171 : 35
Nop -> from 171 : 27 to 171 : 35

So it seems that first Nop pair, with no other statement occupying the the same range, may be what it being detected as uncovered (unlike the second Nop, which overlaps the immediately preceding ExpressionStatement exactly).

The equivalent IL looks like

.method assembly instance class Tinesware.Rules.Assist.Inconclusive CheckStatic(class [Microsoft.Cci]Microsoft.FxCop.Sdk.Method fn, class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [System.Xml.Linq]System.Xml.Linq.XElement> xml) cil managed
{
.custom instance void [Tinesware.Infrastructure]Tinesware.Infrastructure.CoverageExemptionAttribute::.ctor() = { Points=int32(0x10) Justification=string('Operationally tested') }
.custom instance void [mscorlib]System.Diagnostics.CodeAnalysis.SuppressMessageAttribute::.ctor(string, string) = { string('Microsoft.Usage') string('CA1801:ReviewUnusedParameters') MessageId=string('xml') Justification=string('Required signature') }
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = { new int32[int32(2)] { int32(1), int32(1) } }
.maxstack 9
.locals init (
[0] class Tinesware.Rules.Assist.Inconclusive inconclusive,
[1] class [Microsoft.Cci]Microsoft.FxCop.Sdk.Method 'method',
[2] class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> option,
[3] class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> option2,
[4] class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> option3,
[5] class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> option4,
[6] class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> option5,
[7] class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> option6,
[8] class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> option7,
[9] class Tinesware.Rules.Assist.Inconclusive inconclusive2,
[10] class [FSharp.Core]System.Tuple`2<string, string> tuple,
[11] class [Microsoft.Cci]Microsoft.FxCop.Sdk.Method method2,
[12] class [Microsoft.Cci]Microsoft.FxCop.Sdk.ModuleNode node)
L_0000: nop
L_0001: nop
L_0002: ldarg.1
L_0003: stloc.1
L_0004: ldloc.1
L_0005: call class [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1<class [FSharp.Core]Microsoft.FSharp.Core.Unit> Tinesware.Rules.Patterns::|SimpleGetter|_|(class [Microsoft.Cci]Microsoft.FxCop.Sdk.Method)
L_000a: stloc.2
L_000b: ldloc.2
L_000c: brfalse.s L_0010
L_000e: br.s L_0012
L_0010: br.s L_001d
L_0012: call class Tinesware.Rules.Assist.Inconclusive Tinesware.Rules.Assist.Inconclusive::get_Resolved()
L_0017: nop
view raw gistfile1.cs hosted with ❤ by GitHub

which does indeed have a pair of Nop opcodes at the start (lines 21 and 22).

So, that looks like another heuristic to add - removing unvisited code point records that correspond to nothing but a Nop.

The February 2010 CTP generates identical code here.

Thursday, November 19, 2009

NDepend -- a belated "kicking the tyres" review

While there may be subtle details changed by the February 2010 CTP, the broad thrust of how F# looks to a C# directed tool remains. The mass of compiler inserted locals which skews the analysis certainly containues to hold.

First off, a little confession -- while I'm a C# coder by day, by night I use a whole variety of other languages instead; so I don't have a large corpus of my own code to use it against; and having installed my gift copy of NDepend v2.12.1.3123 (Pro Edition) on my personal development machine at home, there are certain ethical issues involved in getting it to run over code from the day job as-is.

I do have an on-going project in F#, which at least the tool will consume after a fashion (as the tool doesn't know .fsproj files from Greek, analysis has to proceed by selecting a number of assemblies to work with), so my experiences are based on that.

First impressions

At one extreme, tools such as FxCop or StyleCop provide a report that indicates a number of point changes to make to code with pretty much zero configuration to the tools; at the other extreme, nUnit and nCover are things that require you to configure (code) pretty much everything, and responding to the output is a matter of judgement. NDepend is somewhere in the middle -- you can run it with default settings and get some analysis of your code, but the settings are completely malleable; and the response can in places be a judgement call too.

There are some rules in the default set which overlap, and in places contradict, guidance from FxCop or StyleCop -- especially in matters like naming conventions. This latter is the stuff of which religious arguments are made (my own choice being to leave policing of names to the MSFT tools). Similarly, it invents a method-level NDepend.CQL.GeneratedAttribute to mark generated code when we already have System.Runtime.CompilerServices.CompilerGeneratedAttribute and the class-applicable System.CodeDom.Compiler.GeneratedCodeAttribute

Some of the conditions tested (setting aside the false positives out of F#) are useful complements to FxCop, such as flagging places where code visibility could beneficially be adjusted to better encapsulate code, and are essentially resolved by point fixes in many cases.

The real value comes in the rules that examine the structure of the code (even if, again, the precise thresholds used are potentially subjects for debate).

Live test

I ran NDepend against the latest drop of my FxCop rules project; this is possibly a small sample of code, and in some respects an edge case (with strong coupling to framework or FxCop classes, and very low abstraction).

Being F#, I have to just supply the assemblies, and cannot get source level analysis (such as cyclomatic complexity or comment density metrics).

Aside from the language difficulty the one place I have had trouble is making sense of the coverage file integration -- the nCover output I supply seems to get ignored; and the linked-to tutorial refers to such an earlier version that the UI doesn't match at all.

Interpretation (and F# peculiarities)

With no interfaces defined in the project, I seem to trigger a fencepost error in the report for class statistics

Stat# OccurrencesAvgStdDevMax
Properties on Interfaces 0 Interfaces 00-1 properties on
Methods on Interfaces 0 Interfaces 00-1 methods on
Arguments on Methods on Interfaces 0 Methods 00-1 arguments on

and there are some places where the names of F# methods may be problematic -- for a number of rules I get a "WARNING: The following CQL constraint is not satisfied. 1 methods on 358 tested match the condition.", but the name of the offending method I have to find for myself:













methods# lines of code (LOC)Full Name
Sum:2
Average:2
Minimum:2
Maximum:2
Standard deviation:0
Variance:0

As I've been posting in my "F# under the covers" series of posts, the IL that gets generated is, shall we say, interesting; and that is one of the things that gets shown up by rules tuned to C#. Indeed, the very first run I made was what provoked a one-shot into an on-going series. For example, this method

member private self.CheckStatic (fn:Method) (xml:Option<XmlNode>) =
let result =
match fn with
| SimpleGetter -> Resolved
| SimpleSetter -> Resolved
| SimpleConstructor -> Resolved
| Stub -> Resolved
| CSharpVoidStub -> Resolved
| CSharpThrowsStub -> Resolved
| CSharpConstStub -> Resolved
| _ -> Open
if result = Resolved then
MarkXml xml StaticAnalysisExempt Int32.MaxValue "Static"
match CoverageExemptionRule.GetAttribute ("Tinesware.Infrastructure", "CoverageExemptionAttribute") fn fn.DeclaringType.DeclaringModule with
| None -> ()
| _ -> self.Problems.Add(new Problem(self.GetNamedResolution("attributeStatic", fn.FullName), fn))
result
view raw gistfile1.fs hosted with ❤ by GitHub

has 11 variables and a cyclomatic complexity (in IL) of 12

That method -- which is used in

member private self.ProcessMethod fn doc =
let xml = FindXmlForMethod (FindSignatureForMethod fn) doc
let uncovered = EstimateUncovered fn xml
fn >+?? xml
>?> CoverageExemptionRule.ExcludeAngleBrackets
>?> SkipFuns
>?> CoverageExemptionRule.CheckCompilerGenerated
>?> CoverageExemptionRule.CheckCompilationMapping
>?> self.CheckStatic
>?> (self.CheckExemption uncovered)
>?> (self.Fallback uncovered doc) |> ignore
view raw gistfile1.fs hosted with ❤ by GitHub

where >?> and >+?? are combinators, gets detected as unused ("No Afferent Coupling -> The method is not used in the context of this application.")

A number of rules -- such as "A stateless type might be turned into a static type" -- show up quite how many compiler generated classes there are; both the ones with @-line-number names (e.g. Patterns+MapToTypes@41) for F# funs, and simple nested classes for algebraic types. In production use of NDepend on F#, these rules would have to be extended so such types could be filtered automatically.

For other false positives, such as the spurious numbers of locals, it would be beneficial to filter on a suppression attribute (along the lines of System.Diagnostics.CodeAnalysis.SuppressMessage) with a string property that the rule could match on.

Overall

Another useful and interesting tool in the general .net developer's armoury. Even out of the box, I could see that it would provide useful automation to supplement a manual code review process, though it would require a degree of customization to fit into an established build and coding standards regime.

Links for 19-Nov

New from the PDC:

WPF - from C# to F#.

Automating signing of PoSh scripts.

Prefer structured lifetimes – local, nested, bounded, deterministic.

Embedding IronPython in Silverlight.

Mono tools for Visual Studio.

Moonlight futures.

Google's 'go' -- is it the new VB?

Friday, November 13, 2009

Nature notes

Mild wet and very windy up from the south, so mild that, when I went downstairs to get a bedtime drink last night, I found that the cats had brought a frog in, and were just sitting around looking at it.

And while a few clear cool nights have just about ended the tomatoes for the year, it was only a couple of days ago I picked the last couple of properly ripe fruit off the Alicante in the greenhouse. The chilis are still going wild, and we're just getting the first capsicums ripening.

And of course even the tender plants like the fuchsia are going strong.

Thursday, November 12, 2009

Alpha-encoding file versions

When building installers the UpgradeVersion must have a unique property value that is an installer public property (upper-case alpha). So, what better way of adding uniqueness than making it have the form "product name + product version" with the version suitably encoded...

So, a script for turning a file version (4 x 16bit ints) encoded as a System.Version into a short alpha string, assuming that Major and Minor will be small, and that common approaches are to step Build, to use a stepped Build plus date-stamped Revision, or a timestamp Build and Revision --

from System import Version
def encode13(value, flag):
v = value + (13 if flag else 0)
return chr(ord('A')+v)
def encode25(value):
return chr(ord('A')+value)
def encodePrimary(value):
def encodePrimaryHelper(value, result):
digit = value % 13
rest = value /13
result += encode13(digit, rest)
if rest:
return encodePrimaryHelper(rest, result)
else:
return result
return encodePrimaryHelper(value, '')
def encodeByte(value):
if 0 == value:
return 'Z'
else:
return encode25(value/25)+encode25(value%25)
def encodeWord(value):
bottom = value % 256
top = (value/256) % 256
return encodeByte(top)+encodeByte(bottom)
def encodeVersion(version):
value = (encodePrimary(version.Major) +
encodePrimary(version.Minor) +
encodeWord(version.Build))
if version.Revision:
value += encodeWord(version.Revision)
return value
if __name__ == '__main__':
import unittest
class TestEncode(unittest.TestCase):
def testPrimary(self):
self.assertEqual(encodePrimary(0), 'A', 'encode 0')
self.assertEqual(encodePrimary(11), 'L', 'encode 11')
self.assertEqual(encodePrimary(13), 'NB', 'encode 13')
self.assertEqual(encodePrimary(32), 'TC', 'encode 32')
self.assertEqual(encodePrimary(169), 'NNB', 'encode 13')
def testByte(self):
self.assertEqual(encodeByte(0), 'Z', 'encode 0')
self.assertEqual(encodeByte(1), 'AB', 'encode 1')
self.assertEqual(encodeByte(25), 'BA', 'encode 25')
self.assertEqual(encodeByte(255), 'KF', 'encode 255')
def testVersion(self):
self.assertEqual(encodeVersion(Version('1.0.0.0')),
'BAZZ', 'encode 1.0.0.0')
self.assertEqual(encodeVersion(Version('1.0.0.1')),
'BAZZZAB', 'encode 1.0.0.1')
self.assertEqual(encodeVersion(Version('1.0.33.0')),
'BAZBI', 'encode 1.0.33.0')
self.assertEqual(encodeVersion(Version('1.0.3369.23350')),
'BAANBQDQCE', '1.0.3369.23350')
unittest.main()
view raw gistfile1.py hosted with ❤ by GitHub

where the first two facets are encoded as telescoped base-13 (with a bit to say "more to come"), and the second two are encoded as pairs of bytes -- Z for a zero byte or as a 2-character base-25 representation if non-zero, with a zero Revision being dropped. This gives 10 characters in a plausible worst case, or as low as 5 in some conventions (stepped build numbers only); as opposed to the naive 64-bit as all-base-26 which would give 11 characters always.

Wednesday, November 11, 2009

“Hello GTK#” from the latest IronPython and F#

A little post to record a short bit of spiking with GTK# as UI toolkit, porting the simple C# examples from here to my preferred .net languages. Neither of these explorations are totally novel -- there are examples in either language to Google, but not all of them recorded all the details of the slight rough spots that needed a little working (and they were often not at all recent, either).

For IronPython 2.6 latest with GTK# 2.12.9-2, running the programs as ipy Program.py, the code looks like

import clr
clr.AddReference('gtk-sharp')
from System import *
from Gtk import *
def OnDelete(sender, args):
Application.Quit()
args.RetVal = True
click_count = 0
def OnButtonClick(sender, args):
global click_count
click_count += 1
Console.WriteLine("Button Click {0}", click_count)
def RealEntryPoint():
## Create the window
Application.Init();
wnd = Window("IronPython Main Window")
## Add a vertical box and a button
box = VBox();
ok_btn = Button("Ok")
ok_btn.Clicked += OnButtonClick
box.PackStart(ok_btn, False, False, 0)
wnd.Add(box)
## Set up the window delete event and display it
wnd.DeleteEvent += OnDelete
wnd.ShowAll()
## And we're off!...
Application.Run();
if __name__ == "__main__":
RealEntryPoint()
view raw gistfile1.py hosted with ❤ by GitHub

where the dynamic nature of the language means we can lose a lot of the declaration clutter. We just have to explicitly reference the main GTK# assembly (which is GAC'd by the GTK# installer), and away we go.

F# was almost, but not quite, as smooth. You have to add references to additional assemblies atk-sharp and glib-sharp, and the types are a little more explicit:

open System
open Gtk
let mutable click_count = 0
let OnClick (btn : Button) =
click_count <- click_count + 1
// Console.WriteLine("Button Click {0}", click_count)
btn.Label <- String.Format("Button Click {0}", click_count)
let OnDelete (sender:obj) (args:DeleteEventArgs) =
Application.Quit()
args.RetVal <- true
[<EntryPoint>]
let main a =
// Create the window
Application.Init();
let wnd = new Window("F# Main Window")
// Add a vertical box and a button
let box = new Gtk.VBox()
let ok_btn = new Button("Ok")
//ok_btn.Clicked += new EventHandler(OnButtonClick)
ok_btn.Clicked.AddHandler(fun s a -> OnClick ok_btn)
box.PackStart(ok_btn, false, false, (uint32 0))
wnd.Add(box)
// Set up the window delete event and display it
wnd.DeleteEvent.AddHandler(fun s a -> OnDelete s a) // fun still needed
wnd.ShowAll()
// And we're off!...
Application.Run()
0
view raw gistfile1.fs hosted with ❤ by GitHub

With the project built as a Windows Application, the Console output doesn't show (even to the Output window in Visual Studio), so the code has been changed to update the button caption after clicking. Apart from that, GTK# follows the WinForms eventing model, so wiring up the events is just a matter of adding the appropriate handler functions in the same way as you would normally -- including the effect that the function value OnDelete needs to be coerced to a delegate type of the same signature such as via a wrapper fun as shown, and can't just be added directly.

This program is also not FxCop clean, but there's nothing GTK# specific about the tidying operations required.

As expected, these work unchanged with the 1.9.9.9 CTP

Sunday, November 08, 2009

F# under the covers VIII

I'm in the tidying up stages for the little project I've been working on lately, a set of FxCop rules, mainly aimed at providing some complementary features for nCover-style code coverage, including static analysis for trivial methods -- or compiler generated ones. I've done the red -- add a set of methods that should give expected static analysis results -- and green -- write the simplest implementation that works when given the code base to analyse as a post-build activity. And now I'm on the refactor leg, abstracting out the common activities, taming sprawling methods into tighter ones, and reducing the level of imperative features (perhaps also making the code more idiomatic), even at times managing to delete whole methods.

And while I'm doing so, I'm running various tools over the code; FxCop and nCover over a small nUnit run every build, of course, but in turns Reflector and NDepend -- the results of which have been the inspiration for this series of posts.

And as we have seen, the code emitted by the F# compiler in its various iterations is unlike that coming from the more traditional .net langauges, in a way that doesn't always play nice with tools developed against C# or VB.net (and perhaps C++/CLI).

NDepend will deserve an essay of its own in due course -- part of the quid pro quo for the copy that Patrick Smacchia has generously donated -- but today's point of interest is that some IL code generated from F# cannot (at least with current Reflector builds) be easily folded back into C# source.

Take this active pattern used to match a property getter method that just returns a backing field with a name related to the property name, which is at a current intermediate stage of refactoring

let (|SimpleGetter|_|) (fn:Method) =
if (not fn.IsAccessor) || (not (fn.Name.ToString() @ "get_")) then None
else match MapToTypes fn.Body.Statements with
| [NodeType.Block; NodeType.Block] ->
match (fn.Body.Statements.[0], fn.Body.Statements.[1], fn) with
| CSharpGetterInterior -> Some ()
| _ -> None
| [NodeType.Block] ->
match (fn.Body.Statements.[0], fn) with
| SimpleGetterInterior -> Some()
| _ -> None
| _ -> None
view raw gistfile1.fs hosted with ❤ by GitHub

where MapToTypes turns an FxCop StatementCollection into a list of their corresponding NodeTypes, and @ wraps String.StartsWith with ordinal comparison.

Confronted with a request to decompile into C#, Reflector 5.1.6.0 asks me to file a bug report (which I have done) for an exception "Invalid branching statement for condition expression with target offset 002A."

Now, as far as I can tell from the IL, this is part of line 2 where it is balking

L_0000: nop
L_0001: ldarg.0
L_0002: call instance bool [Microsoft.Cci]Microsoft.FxCop.Sdk.Method::get_IsAccessor()
L_0007: brtrue.s L_000b
L_0009: br.s L_000d
L_000b: br.s L_0011
L_000d: ldc.i4.1
L_000e: nop
L_000f: br.s L_002a
L_0011: ldarg.0
L_0012: call instance class [Microsoft.Cci]Microsoft.FxCop.Sdk.Identifier [Microsoft.Cci]Microsoft.FxCop.Sdk.Member::get_Name()
L_0017: callvirt instance string [mscorlib]System.Object::ToString()
L_001c: ldstr "get_"
L_0021: call bool Tinesware.Rules.Assist.Local::op_Append(string, string)
L_0026: ldc.i4.0
L_0027: ceq
L_0029: nop
L_002a: brfalse.s L_002e
L_002c: br.s L_0030
L_002e: br.s L_0032
L_0030: ldnull
L_0031: ret
...
view raw gistfile1.cs hosted with ❤ by GitHub

Presumably -- I would have to build some equivalent C# code to validate -- the brfalse.s is not one that C# (or any of the other languages Reflector knows of) would actually emit. Certainly it seems that the new F# CTP compiler is fond of emitting it, and it breaks Reflector every time it does.

Another good reason for avoiding too many imperative constructs in your F# code, it would seem.


Later: this C# code that approximates the above F#

public static int? SimpleGetter(System.Reflection.MethodInfo fn)
{
if (!fn.IsSpecialName || !(fn.Name.StartsWith("get_"))) return null;
return fn.Name.Length;
}
view raw gistfile1.cs hosted with ❤ by GitHub

compiles to

L_0000: nop
L_0001: ldarg.0
L_0002: callvirt instance bool [mscorlib]System.Reflection.MethodBase::get_IsSpecialName()
L_0007: brfalse.s L_001b
L_0009: ldarg.0
L_000a: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name()
L_000f: ldstr "get_"
L_0014: callvirt instance bool [mscorlib]System.String::StartsWith(string)
L_0019: br.s L_001c
L_001b: ldc.i4.0
L_001c: stloc.1
L_001d: ldloc.1
L_001e: brtrue.s L_002c
L_0020: ldloca.s CS$0$0002
L_0022: initobj [mscorlib]System.Nullable`1
L_0028: ldloc.2
L_0029: stloc.0
L_002a: br.s L_003f
L_002c: ldarg.0
L_002d: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name()
L_0032: callvirt instance int32 [mscorlib]System.String::get_Length()
L_0037: newobj instance void [mscorlib]System.Nullable`1::.ctor(!0)
L_003c: stloc.0
L_003d: br.s L_003f
L_003f: ldloc.0
L_0040: ret
view raw gistfile1.cs hosted with ❤ by GitHub

which indeed, as I suspected, uses a brtrue.s for the final branch out of the if statement (though there is a brfalse.s to perform the short-circuiting.

Code generation here is unchanged in 1.9.9.9 from the previous CTP.

Tuesday, November 03, 2009

F# under the covers VII

Working with nCover to examine the number of code-points represented by any given method, the October CTP made a large number of changes -- some up, some down -- to the code generated by the compiler.

Most interesting is the change to attribute setters, which used to be NOP;assign;return and are now just assign;return. Also the name of the implicit backing field has changed to be the property name with an appended '@', rather than being a case-mangled version of the property name.

(As always, talking about Debug builds).

Later ... This one is a bit too cryptic for me to decipher what I was writing about. Code like this:

type internal Context = {
Line : int;
Column : int;
EndLine : int;
EndColumn : int }
view raw gistfile1.fs hosted with ❤ by GitHub

Has internal fields like Line@ still at 1.9.9.9; and property setters as in

[<DefaultValue(true)>]
val mutable private points : int
member self.Points with get() = self.points and set(v) = self.points <- v
view raw gistfile1.fs hosted with ❤ by GitHub

compile to the NOP-bearing

.method public specialname instance void set_Points(int32 v) cil managed
{
.maxstack 4
L_0000: nop
L_0001: ldarg.0
L_0002: ldarg.1
L_0003: stfld int32 MyTestClass::points
L_0008: ret
}
view raw gistfile1.cs hosted with ❤ by GitHub

with the Feb 2010 CTP.

Links for 3-Nov

Vista presentation mode tips and enabling applications.

Attaching a debugger on process start.

Simplicity is complicated.

F# immutable queue and range.

Validating JavaScript syntax with a .net wrapper.

Underscore -- a functional programming utility library for JavaScript

Monday, November 02, 2009

Nature notes

Driving home, after dining with colleagues, in the hard bright moonlight of a Hunter's Moon, I actually saw a live badger ambling along the verge at one point, a rare spotting of some of the larger wildlife around here.

Quite how starkly bright a winter full moon can be always comes as a surprise.

Also, still harvesting a last few tomatoes from both outdoor and greenhouse plants.