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 Context = {
         Line : int;
         Column : int;
         EndLine : int;
         EndColumn : int }

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

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

Consider the following simple C# property

public static FileShare get_Options()
{
    return (FileShare.Delete | FileShare.Write);
}

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 
}

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

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 
}

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

  [<CoverageExemption( Points = 1, Justification = "Code generated is not a Literal")>] 
  static member Options = System.IO.FileShare.Write ||| System.IO.FileShare.Delete

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

[CoverageExemption(Points=1, Justification="Code generated is not a Literal")]
public static FileShare Options
{
    get
    {
        return (FileShare.Write | FileShare.Delete);
    }
}

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

public static FileShare Options
{
    [CoverageExemption(Points=1, Justification="Code generated is not a Literal")]
    get
    {
        return (FileShare.Write | FileShare.Delete);
    }
}

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 [<CoverageExemption( Points = 1, Justification = "Code generated is not a Literal")>] get() = 
       System.IO.FileShare.Write ||| System.IO.FileShare.Delete

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.

Tuesday, November 24, 2009

Links for 24-Nov

Simon Willison is excited by Node.js

PPK remarks that Apple is not evil -- and may be surprisingly cunning.

Luke Hoban’s F# for Parallel and Asynchronous Programming (PDC video)

Bart Czernicki’s Silverlight 3 and F# Support in Visual Studio 2010

Pense-moi's Immutable queue library for F#

Sunday, November 22, 2009

F# -- a few notes and something to play with

Seq.take<'T>

While F# documentation is there on MSDN, it is a little sketchy. For example, take Seq.take<'T>:

Return the first N elements of the sequence.

Contrast, for example, Scala's Seq[A].take

Returns a sequence consisting only over the first n elements of this sequence, or else the whole sequence, if it has less than n elements. (non-strict)

So, what happens when you take too much in F#?

Answer: an exception is thrown indicating too few elements in the collection.

It's not just Scala -- Haskell, Erlang lists:sublist and -- most tellingly -- OCaml all define an "up to N" behaviour for a take style operation. (So I've submitted this one as a bug report).

Later: Actually what I was looking for is Seq.truncate.

And that was one of today's little gotchas…

Generated names for funs

In the February CTP release the class representing a fun in method MyMethod of class My.FullyQualified.ClassName at line ### was named My.FullyQualified.ClassName+MyMethod@### with clashes resolved by adding an incrementing count suffix like -#.

In the October CTP, the name is now <StartupCode$My-FullyQualified>.$ClassName+MyMethod@### plus possible suffix as before.

A release

I mentioned yesterday about getting close to calling a little toolset I've been working on to something I could call 1.0 state -- which basically means it seems to do what I want it to do in test for the intended feature set, I have no outstanding bugs, and all the low-hanging refactoring and more has been done (though it's not complete and really never can be, and it could probably do with more comments).

So, it works with nCover format coverage files and FxCop to provide coverage analysis and reporting -- separating out code that isn't covered in test as to why that might be the case, like for its own solution, a mix of C# and F# projects:

Legend

Uncovered code
User exempted
Generated code
Statically analyzed
Covered code

Modules summary

CSharpTests
100%
Tinesware.Infrastructure
10.53% 10.53% 78.95%
Tinesware.InfrastructureTests
3.57% 7.14% 25% 64.29%
Tinesware.Rules
9.31% 2.09% 0.48% 88.12%
Tinesware.TestData
3.23% 24.19% 12.9% 62.9% 3.23%

Use it like this

rem Generate coverage.xml
ncover.console.exe nunit-console.exe [unit test assembly] /noshadow //reg //a 
rem run FxCop over the files in the same directory
FxCopCmd.exe /f:[file to analyse] [/f:[file to analyse] ... ] /rule:\path\to\Tinesware.Rules.dll  "/rule:\path\to\Microsoft FxCop 1.36\Rules" ...

Then view the generated AnalyzedCoverage.xml file in a browser using the supplied coverage.xsl file (copied to the same folder).

More discussion of the road ahead on my other blog.

Source and binaries are in my Works In Progress folder here. Bug reports and enhancement requests welcome -- turnround may not be speedy as I'll probably pick up something else from my stack to work on next, but will be added to the queue.

Saturday, November 21, 2009

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

They're coming thick and fast now...

Define a record type such as

type Context = {
         Line : int;
         Column : int;
         EndLine : int;
         EndColumn : int }

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; }
}

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);
}

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 

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 state I feel comfortable with calling a 1.0 release, 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):

  member private self.CheckStatic (fn:Method) (xml:Option<XElement>) =
    if  match fn with
        | SimpleGetter -> Resolved
        | SimpleSetter -> Resolved
        | SimpleConstructor -> Resolved
        | Stub -> Resolved
        | CSharpVoidStub -> Resolved
        | CSharpThrowsStub -> Resolved
        | CSharpConstStub -> Resolved
        | _ -> Open
        = 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))
          Resolved
          
    else
          Open

Although my tests give a match for every case in the pattern -- and nCover shows a visit for every line from 168 to 176, 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 

which does indeed have a pair of Nop opcodes at the start.

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

Thursday, November 19, 2009

NDepend -- a belated "kicking the tyres" review

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

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

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()

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()

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

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.

Monday, November 09, 2009

Links for 9-Nov

Windows Identity Framework ("Geneva") hits RC.

Trampoline recursion in C#.

Cross-browser XUL and SVG library.

CComPtr<T> type safety issue.

F#:

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

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 
...

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;
        }

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 

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.

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).

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