Kamis, Oktober 23, 2008


Script# brings the C# developer experience (programming and tooling) to Javascript/Ajax world. This post shares a project for enabling script authoring via C#...

Script# brings the C# developer experience (programming and tooling) to Javascript/Ajax world. Yep, I am finally publicly sharing a spare time project I've been working on the side in an off and on manner for some time now.

A Better Scripting Environment

The fundamental goal was to improve the state of the art in scripting. This goes way beyond the usual things that come to mind at first thought: compile-time checking, and intellisense or statement completion. I do think these are super-useful, and would be valuable in and of themselves. As we worked on Atlas we were also thinking of a great tools experience, and we explored different approaches and technologies for achieving this. Script# is a prototype of one such exploration. It attempts to address some programming environment requirements:

  • A clean language with the natural constructs. Today with script, you can go a long ways in simulating OOP (classes, interfaces, inheritance etc.), but the simulations are thrown at the face of the developer. Various C# constructs such as properties, events, and syntax for defining inheritance go a long way in making the code readable. Similarly modifier keywords like public vs. protected vs. internal, sealed vs. virtual etc. help define a better object model. All of this can be brought into the scripting world.

  • Easier refactoring and exploration. Script development could benefit immensely from the refactoring, and class browsing support already present in the IDE and in tools such as .NET Reflector. Both lend themselves to having a more manageable code base over time.

  • Ability to generate documentation. Again doc-comments from C# and the existing infrastructure could be leveraged here.

  • Ability to customize the script code easily. For example, I'll show debug vs. release and minimization below. The same idea could apply to building a version of the script that had more error checking or logging built in when you do need to run diagnostics. The approach could also be used to include instrumentation for the purposes of profiling, measuring code coverage, etc. Another interesting aspect of this project is that it will be able to generate script catering to multiple script profiles such as the current Javascript language, as well as Javascript 2 when it appears.


I'll use the Ajax version of the timeless Hello World scenario to illustrate the model. Imagine a simple page with textbox, button and label:

<input type="text" id="nameTextBox" />
<input type="button" id="okButton" value="OK" />
<span id="helloLabel"></span>

Now I want to write the script for this page to add some UI logic. I'll create HelloWorld.cs with the following code to handle the button click, extract the name entered into the textbox, make a request using XMLHttp and finally display the result in a label.

using System;
using ScriptFX;
using ScriptFX.UI;

namespace HelloWorld {

public class HelloWorldScriptlet : IScriptlet {

private Button _okButton;
private TextBox _nameTextBox;
private Label _helloLabel;

private XMLHttpRequest _request;

public void Start() {
_okButton = new Button(Document.GetElementById("okButton"));
_nameTextBox = new TextBox(Document.GetElementById("nameTextBox"));
_helloLabel = new Label(Document.GetElementById("helloLabel"));

_okButton.Click += new EventHandler(OnOKButtonClick);

private void OnOKButtonClick(object sender, EventArgs e) {
Callback completedCallback = new Callback(this.OnRequestComplete);

_request = new XMLHttpRequest();
_request.Onreadystatechange = Delegate.Unwrap(completedCallback);
_request.Open("GET", "Hello.axd?name=" + _nameTextBox.Text, /* async */ true);

private void OnRequestComplete() {
if (_request.ReadyState == 4) {
_request.Onreadystatechange = null;

string greeting = _request.ResponseText;
_helloLabel.Text = greeting;

Basically this shows a couple of super-simple class HelloWorldScriptlet class. It's a pleasure to write it in C#. Now its time for some Script# magic.

ssc /ref:sscorlib.dll /ref:Script.ScriptFX.Core.dll /debug /out:HelloWorld.js HelloWorld.cs

Running the Script# compiler (ssc.exe) generates HelloWorld.js which looks like this:


// HelloWorld.HelloWorldScriptlet

HelloWorld.HelloWorldScriptlet = function Scenarios_HelloWorldScriptlet() {
HelloWorld.HelloWorldScriptlet.prototype = {
_okButton: null,
_nameTextBox: null,
_helloLabel: null,
_request: null,

start: function Scenarios_HelloWorldScriptlet$start() {
this._okButton = new ScriptFX.UI.Button(document.getElementById('okButton'));
this._nameTextBox = new ScriptFX.UI.TextBox(document.getElementById('nameTextBox'));
this._helloLabel = new ScriptFX.UI.Label(document.getElementById('helloLabel'));
this._okButton.add_click(new Delegate(this, this._onOKButtonClick));

_onOKButtonClick: function Scenarios_HelloWorldScriptlet$_onOKButtonClick(sender, e) {
var completedCallback = new Delegate(this, this._onRequestComplete);
this._request = new XMLHttpRequest();
this._request.onreadystatechange = Delegate.unwrap(completedCallback);
this._request.open('GET', 'Hello.axd?name=' + this._nameTextBox.get_text(), true);

_onRequestComplete: function Scenarios_HelloWorldScriptlet$_onRequestComplete() {
if (this._request.readyState == 4) {
this._request.onreadystatechange = null;
var greeting = this._request.responseText;

HelloWorld.HelloWorldScriptlet.registerClass('HelloWorld.HelloWorldScriptlet', null, ScriptFX.IScriptlet);

Notice how the class is converted into its Javascript equivalent: a function, and methods on its prototype. Further, notice how this automatically generates calls to register the function as a class. The compiler provides a nice natural model for declaring the inheritance hierarchy. Other things to call out are conversion of of C#-based event and property accesses with Javascript simulations. Essentially, let the compiler do the hard work, so you don't have to mentally work against an OOP simulation. Finally, since the compiler was passed the /debug flag, it provides a name to each method (anonymous methods aren't friendly in the debugger)... again this happens automagically. There is a lot more to show that a simple sample doesn't get across in terms of the capabilities of the conversion.

The generated code can now be used in the page via a regular <script> tag.

<script type="text/javascript" src="sscorlib.js"></script>
<script type="text/javascript" src="ssfxcore.js"></script>
<script type="text/javascript" src="HelloWorld.js"></script>
<script type="text/javascript">
ScriptFX.Application.Current.run(new HelloWorld.HelloWorldScriptlet());


The Script# compiler can optionally generate an assembly containing the .js file as a resource. An associated server control can then use the ASP.NET WebResources feature to include the scriptlet into the page in a simpler manner.

ssc /ref:sscorlib.dll /ref:Script.ScriptFX.Core.dll /debug /assembly:HelloWorld.dll /out:HelloWorld.js HelloWorld.cs

<nStuff:Scriptlet runat="server"
ScriptAssembly="HelloWorld" ScriptletType="HelloWorld.HelloWorldScriptlet" />

The compiler has the notion of debug and release builds.

ssc /ref:sscorlib.dll /ref:Script.ScriptFX.Core.dll /minimize /out:HelloWorld.js HelloWorld.cs

Running the compiler without /debug and with /minimize produces a release build as shown below (I've intentionally added some line breaks for display purposes):

this.$0=new ScriptFX.UI.Button(document.getElementById('okButton'));
this.$1=new ScriptFX.UI.TextBox(document.getElementById('nameTextBox'));
this.$2=new ScriptFX.UI.Label(document.getElementById('helloLabel'));
this.$0.add_click(new Delegate(this,this.$4));},
var $2=new Delegate(this,this.$5);
this.$3=new XMLHttpRequest();
var $0=this.$3.responseText;

As you'll notice all insignificant whitespace has been trimmed out. Furthermore, there is minimization of identifiers (which are amongst the biggest contributors to script size) with identifiers named as $0, $1 etc. What is super exciting is that the Script# compiler can use all the information present in C# source code to allow it to maximize the minimization. This includes information about which members are internal/private vs. which ones are public. For example, the _okButton member of the class is now $0 and the OnRequestComplete method is $5.

Beyond generation of script in a smarter way, Script# helps improve the script authoring model within the IDE. Despite programming the DOM for several months now, I still need a browser window constantly open to browse MSDN reference documentation on the DOM. With Script# here is what I get:

Specific things to notice are the intellisense and statement completion popups and tooltips offered by the C# editor, as it reflects on the sscorlib assembly referenced by this project.

Essentially, I've introduced the notion of a Script# class library project, and associated msbuild targets file and task for integrating the experience into Visual Studio 2005. Building builds both a .dll assembly, and an associated .js file, that you can now deploy with your web project.

I have put together a 10 min video of this scenario. This is an experiment in itself with me putting out a video to get some concepts out (so feedback appreciated on this as well).

How does it work?

Essentially the Script# compiler is a C# compiler that generates Javascript instead of IL. A key driving goal of the design is to produce readable Javascript that you may have authored yourself, and would be ok deploying into real apps. Hence the translation works at the C# level, as opposed to converting from IL to script, and the converter doesn't add any levels of abstraction itself.

There are a set of C# things that don't make sense (eg. lock, unsafe etc.) and set of things I don't support yet (like generics). The converter does however provide support for key C# patterns such as foreach, delegates, and things like Debug conditionals. The associated class library (sscorlib, equivalent of mscorlib) is also tuned to Javascript. It doesn't contain the same stuff as mscorlib does. The approach I have taken is not to create a converter that converts an arbitrary existing application written in C#, and convert it to Javascript with something like a winforms abstraction layer. Instead, like Atlas, the idea behind Script# is oriented at providing an engineering approach and superior environment to developing applications using HTML/CSS and Javascript in a more productive, scalable and maintainable manner.

There is a small set of Javascript specific things (specifically closures and more functional style programming) that aren't supported in the conversion. I think there are equivalent alternatives, but I'd be interested in feedback nonetheless. The implementation does allow you to hand-code some Javascript and expose it to the C# code in the form of assembly metadata. This provides a nice out for incorporating something that exists already, or something that needs more hand tuning. I'll go into this some in a later post.

Some historical context and downloadable bits...

A number of people have wondered if our OOP-based type system in Atlas was a precursor to enabling C#-based development in the browser, and I'll answer to that directly now. The idea of using C# to author script emerged at almost the same time we started actively writing script, and I missed everything I took for granted from C#. In December, while on vacation, I decided to finally get down to writing a compiler that could enable this.

Since then, the work has proceeded to a point where it can be used to generate useful script. I was planning on posting an announcement just last week. It's very coincidental that the folks at Google have been thinking along similar lines. I guess I should have posted this stuff a few days back :-) Regardless, I have an initial release ready... You can download the Script# compiler along with the sample. Keep in mind it is raw in quite the literal sense (and if you do try it, make sure your code compiles as valid C# first). I'll need some more time to get it to a crisper state, providing better integration into Web projects, and simplifying some key C# scenarios. This will happen over time as this is a spare time prototype. In the meantime, the download will give you a sense of the experience. I'm definitely open to feedback, and look forward to comments.

Scripting Challenges and Future Vision

Recently, I posted my wish list for the browser and scripting world, and I wanted to speak to things relating and coming together from an overall vision perspective. At the end of the day, the end result of translation is still script. There are still some unsolved challenges in terms of a limited runtime environment in the browser, cross-browser differences etc. Furthermore translation-based models add a wrinkle to the debugging experience. Script# provides a tool to incrementally improve script authoring in the short term. We want to provide great tools and frameworks that target native scripting as it exists today with things like Atlas. Longer term, the real solution for rich Web applications is a combination of WPF/E (as shown at MIX06), and a cross-platform CLR that can be hosted in the browser allowing direct authoring and execution of managed code and fully benefiting from the model.

copy paste dari :

0 komentar: