FxCop for Ajax Code

Script#-based Ajax development enables you to repurpose existing .NET tools - this posts shows how you can use FxCop to perform static code analysis for script code...

FxCop is a great tool to help ensure consistency of .NET code and to help push quality upstream in the development process by reinforcing design guidelines and flagging some potential issues that can be detected through static analysis of your assemblies. Last week, the FxCop team released v1.36 of the standalone tool.

The design guidelines are documented and explained in the Framework Design Guidelines book, which also has a section dedicated to FxCop. The interesting thing is a good chunk of the guidelines also apply to Ajax, as they're first and foremost targeted at the general task of creating usable APIs and frameworks. I'd highly recommend familiarizing yourself with them if you're writing .NET code.

This announcement spurred me into demonstrating the integration of this essential .NET tool into the Ajax development process. One of the key value propositions of Script# is enabling you to leverage existing .NET tooling in the context of building script-based applications and components. FxCop is a key scenario in that regard.

I just released v0.5.1.0 of script# which is a minor increment that includes a few bug fixes, the ability to define overloads and methods with optional parameters, and for those using Script# against the Microsoft Ajax library, the corresponding c# assembly has been updated to account for the addition of history APIs in .NET 3.5 SP1. Additionally, I've taken the chance to tweak the project templates and installer ever so slightly to allow you to much more easily use FxCop.

Here is a quick walkthrough, assuming you've installed this latest version of Script# and FxCop. I'll create a super-simple app for the purposes of demonstration that fetches a list of bookmarks via an XMLHttp request (it is Ajax after all) and display the results in an HTML list. Undoubtedly, you'll have a more interesting code-base for FxCop to analyze.

In Visual Studio, create a new Script# ClassLibrary.

Lets add some code. Replace the contents of Class1.cs with the following c# code:

using System;
using System.DHTML;

namespace Bookmarking {

    [Record]
    public sealed class Bookmark {
        public string title;
        public string url;
    }

    public class Bookmarks {

        public static void LoadBookmarks(DOMElement list) {
            string bookmarkService = "Bookmarks.ashx/GetList";

            XMLHttpRequest request = new XMLHttpRequest();
            request.Open("GET", bookmarkService);
            request.Onreadystatechange = delegate() {
                if ((request.ReadyState == 4) && (request.Status == 200)) {
                    Bookmark[] bookmarks =
                        (Bookmark[])Script.Eval(request.ResponseText);

                    foreach (Bookmark b in bookmarks) {
                        AnchorElement anchor = (AnchorElement)Document.CreateElement("a");
                        anchor.Href = b.url;
                        anchor.InnerHTML = b.title;

                        DOMElement listElement = Document.CreateElement("li");
                        listElement.AppendChild(anchor);

                        list.AppendChild(listElement);
                    }
                }
            };

            request.Send(null);
        }
    }
}

For the sake of completeness in this post, here is the debug flavor of the generated script (the release flavor is minimized and condensed):

Type.createNamespace('Bookmarking');

Bookmarking.Bookmarks = function Bookmarking_Bookmarks() {
}

Bookmarking.Bookmarks.loadBookmarks = function Bookmarking_Bookmarks$loadBookmarks(list) {
    var bookmarkService = 'Bookmarks.ashx/GetList';
    var request = new XMLHttpRequest();
    request.open('GET', bookmarkService);
    request.onreadystatechange = Delegate.create(null, function() {
        if ((request.readyState === 4) && (request.status === 200)) {
            var bookmarks = eval(request.responseText);
            var $enum1 = bookmarks.getEnumerator();
            while ($enum1.moveNext()) {
                var b = $enum1.get_current();
                var anchor = document.createElement('a');
                anchor.href = b.url;
                anchor.innerHTML = b.title;
                var listElement = document.createElement('li');
                listElement.appendChild(anchor);
                list.appendChild(listElement);
            }
        }
    });
    request.send(null);
}

Bookmarking.Bookmarks.createClass('Bookmarking.Bookmarks');

In addition, an assembly was generated by the regular c# compiler. If you open up the project folder in Explorer, you'll see a Project.FxCop file in the Properties subfolder. Double-click that, and FxCop should open up. The project has been already set up to analyze the resulting assembly.

You'll see the warning around our use of Script.Eval. For our scenario using Script.Eval to generate an eval expression in script is a legitimate use of the functionality to deserialize the list of bookmarks from the XMLHttp response, so we want to suppress this warning. Lets go back to the c# code, and add a source exclusion. There are also some other warnings suggesting our class should be static (which I'll fix), and on public fields on our Bookmark class (which I'll suppress, given the Bookmark class represents a JSON object returned from the service).

using System;
using System.Diagnostics.CodeAnalysis;
using System.DHTML;

namespace Bookmarking {

    [Record]
    public sealed class Bookmark {

        [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public string title;

        [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
        public string url;
    }

    public static class Bookmarks {

        [SuppressMessage("Script.General", "SS0001:ScriptAvoidEval",
                         Justification = "Using Script.Eval to perform JSON deserialization")]
        public static void LoadBookmarks(DOMElement list) {
            ...
        }
    }
}

If you compile this and re-run the tool, you'll see the warnings goes away. Your code now documents the suppression along with the justification in a standard manner. If Script.Eval did not make sense (because of its perf overhead), you now know about a potential issue, and a possibly better way to go about implementing your code, should that be warranted.

I think the real power comes with creating custom FxCop rules that go beyond the default set of design guidelines and cater to script-specific scenarios. The Script.Eval check is a custom rule that is the first rule I have implemented.

Other initial ideas for basic rules might be to flag use of browser-specific or non-standard script APIs, or creating deep class hierarchies that are costly in script. I'd love to hear from if they have other ideas of APIs or patterns to flags, or design guidelines to look for and enforce via custom rules.


[ Tags: | | | ]
Posted on Thursday, 8/28/2008 @ 6:54 PM | #Script#


Comments

11 comments have been posted.

rekna

Posted on 9/2/2008 @ 6:48 AM
Feature request : anonymous delegate/lamba for inline callbacks... would enhance the way external libraries can be implemented

javascript:
var arr = [ "one", "two", "three", "four", "five" ];

jQuery.each(arr, function() {
$("#" + this).text("My id is " + this + ".");
});

c#
jQuery.each(arr, (int index, string value) => {
$("#"+value).text("My id is "+ value + "." );
});


Or any other suggestion bridje jQuery using elegant syntax?

Nikhil Kothari

Posted on 9/2/2008 @ 6:19 PM
@Rekna - Script# already supports anonymous delegates... If you had a wrapper for jQuery's each function taking in a delegate with the appropriate signature as the 2nd param you could write something like this:

jQuery.Each(arr, delegate(int index, string value) {
...
});

What isn't supported today is the lambda syntax.

dvader

Posted on 9/3/2008 @ 1:15 PM
Hi, Nikhil!

Where are bug reports?
There is a bug under new browser killer :), under Google Chrome. Just incorrect browser detection.

russ

Posted on 9/4/2008 @ 1:39 PM
Thanks for the hard work on Script#.
I've found a bug under Visual Studio 2008: the design time editor for C# scriptlets truncates the scriptlet and/or mangles it, at least as far as I can tell. If i manually code in the source view it all works great but using the editor always kills the script (missing lines or braces, mangled quotes, or even failing to save the script at all).

I would report this on the forums but the codeplex site lists the project as unpublished, even when I'm signed in, so it looks like no one can access the forums.

VV

Posted on 9/11/2008 @ 8:31 AM
Any words about changes in the roadmap (http://projects.nikhilk.net/ScriptSharp/Roadmap-Release.aspx)? Has any plans been changed?

John Lewicki

Posted on 9/16/2008 @ 5:28 AM
Another request for a location to post bug reports or comments!

Lacking that, can any Script# users help me out in figuring out how to use Script.IsNullOrUndefined? Its described in the readme, and available in the sscorlib.js file, but it doesn't seem to be available on the Script class compiled in sscorlib.dll, so I cant refer to it in my .cs file. Am I missing something obvious?

John Lewicki

Posted on 9/16/2008 @ 6:25 AM
Well I figured out Script.IsNullOrUndefined is in fact available is sscorlib.dll. My problem was that I initially had aacorlib as a reference, and then added sscorlib (to check if the method was defined there). The Object Browser in VS2008 gives strange results if both of these assemblies are referenced....

Nikhil Kothari

Posted on 11/11/2008 @ 10:19 PM
The idea is to reference either sscorlib or aacorlib (instead of mscorlib) and not both...

Banner Ads

Posted on 12/12/2008 @ 2:27 AM
Well I figured out Script.IsNullOrUndefined is in fact available is sscorlib.dll. My problem was that I initially had aacorlib as a reference, and then added sscorlib (to check if the method was defined there). The Object Browser in VS2008 gives strange results if both of these assemblies are referenced....

Natural Stone

Posted on 12/12/2008 @ 2:28 AM
Lacking that, can any Script# users help me out in figuring out how to use Script.IsNullOrUndefined? Its described in the readme, and available in the sscorlib.js file, but it doesn't seem to be available on the Script class compiled in sscorlib.dll, so I cant refer to it in my .cs file. Am I missing something obvious?

ghdgh

Posted on 1/21/2009 @ 12:09 PM
dhf
Post your comment and continue the discussion.