Jsm - Another C# to JavaScript translator

Published on Oct 4, 2009
Updated on Oct 25, 2009

Foreword in Russian

Эта статья посвящена задаче преобразования C# кода в JavaScript, которой я занимался последние три недели. Я решил опубликовать статью на английском, дабы привлечь побольше потенциальных читателей. Я уверен, что если вы интересуетесь подобными темами, то языкового барьера возникнуть не должно.

Keywords

Jsm, C#, CSharp, JavaScript, cross-compiler, translator, converter, Mono.Cecil, NRefactory.

About this article

This article is about my research on conversion (translation, cross-compilation) C# code to JavaScript. This is my pet-project named Jsm and hosted at

http://code.google.com/p/lexa-jsm/

Below, after a brief preface I immediately provide you with a simple step by step example (just to give a quick view of a whole thing) and then I go into detail on the translator features and limitations.

What’s the point?

It’s not the secret that the idea isn’t new. There are several projects known for years aiming the same target: Google Web Toolkit, Script#, xaeio, jsc, etc.

So what’s the point of creating another tool?

First, it was interesting for me. It was a kind of a crazy idea (Jsm stands for JavaScript Madness by the way). Second, my intention was to create a translator which would not make anything unnecessary. I mean, it should not emulate properties and events because JavaScript has no native support for them. Yes, there are native getters and setters, but the fact that they don’t work in IE (including 8th version) makes them non-usable.

Another requirement was that the emitted script would be close to its C# original as much as possible.

I like GWT, but it’s all about Java. Script# is a cool project, it produces nice script output and has good documentation but currenlty there is no support for generics and there are some strange limitations on full qualified names. I like generics although there’s no native support for them in JavaScript :)

xaeio and jsc projects both try generate JavaScript direclty from MSIL, which imho is one of the craziest ideas in the world. Just look what is generated by jsc for this simple class.

So, I decided to make my own contribution to the neat idea of generating JavaScript from other high level languages. But, I must say I haven’t decided to create an ajax framework or a comprehensive toolkit. It’s just a cross-compiler and a very basic core library.

First look

I understand that it’s boring to read such articles. So let’s go on and see it in action.

At the moment you need a Windows operating system, installed Visual Studio 2008 and any Subversion client. Unfortunately it’s not possible to build the project against Mono due to a bug in Mono C# compiler.

I assume that you know how to get source code from a Subversion repository. I personally use an old version of SmartSVN, just for historical reasons.

The project’s source code is available at

http://lexa-jsm.googlecode.com/svn/trunk/

After checkout please launch the solution file (Lexa.Jsm.sln) and build it. It may happen that you don’t have NUnit installed. If so, the simpliest resolution is to unload the Lexa.Jsm.Tests project. Finally, launch the FirstLook/compile.bat batch file.

There will be 2 js files generated: corlib.js for the Lexa.Jsm.CoreLibrary assembly and firstlook.js for the Lexa.Jsm.FirstLook assembly. Now you may open them in a text editor and compare generated JavaScript to the original C# source code.

You may wish run the generated code by launching the page.html. If you have a JavaScript debugger enabled in your browser it will break at the first line of the FirstLook.App.Run() method.

Translator features

  • Generated script is close enough to the original C# source
  • Support for generic classes, methods and delegates
  • Support for lambda functions and anonymous methods
  • Capability to define a method body in pure JavaScript via the Native attribute
  • Capability to define inline insertions as static methods marked with the NativeInline attribute
  • Support for foreach loops over arrays and Jsm.Collection implementations

Translator limitations

Source code structure limitations

The following C# capabilities are not supported:

  • Nested classes
  • Nested namespaces
  • Partial classes
  • Anonymous types
  • Extension methods
  • using directives for type alias declaration
  • struct types
  • Properties, events, indexers and operators
  • Multiple catch clauses in try catch finally statements

Overloads are not supported

  • A class cannot have several constructors. Instead, you may use static factory methods or object initializers
  • Within each branch of the type hierarchy, all member names must be unique*
  • Explicit interface implementations are not supported

* However, a static member and an instance member may have the same name. Also, same names may be used for methods with various number of generic type parameters. Virtual and abstract methods are supported.

The following C# syntax is not supported

  • Multidimensional arrays
  • ref and out method arguments
  • params array method arguments
  • goto
  • checked, unsafe, fixed, lock, using blocks
  • stackalloc, yield
  • linq
  • constructions of later versions of C#

Peculiarities of using delegates

Non-static methods having a dependency on this must be marked with the Delegate attribute. For such methods the translator creates an additional closure, so they may be safely passed as delegates to other methods and instances. Unfortunately, these cannot be detected automatically because the translator does not evaluate types of expressions.

For anonymous methods and for lambda functions though, these addidional closures are created automatically where necessary.

Limitations on static initialization

Static fields may only be initialized with primitive values or arrays of primitive values. This also means that static constructors may only contain assignments of such values to static fields.

References to mscorlib are not allowed

The assembly and all its referenced assemblies must not reference mscorlib. Instead, they must reference Lexa.Jsm.CoreLibrary. It’s a special corlib implementation which contains base objects that mimic JavaScript ones.

There are also identities established between some framework names and JavaScript global names: System.Object = Object, System.Array = Array, System.String = String, System.Boolean = Boolean, System.Exception = Error, System.Delegate = Function, System.Int32 and System.Double are both mapped to Number. Transparent wrappers are provided for RegExp, Date and Math objects.

In addition, the Lexa.Jsm.CoreLibrary assembly contains a couple of collection classes and accessors for global JavaScript functions. For instance, you may use the JSM.eval method to invoke the global eval function.

Recently, I’ve added wrappers for some DOM classes and for jQuery 1.3.2.

How the translator works

The translator relies on two third-party libraries: Mono.Cecil for assembly introspection and NRefactory (a part of SharpDevelop) for C# parsing. I had to switch to Cecil because the built-in reflection failed when it met two different System.Object classes.

The command line utility Lexa.Jsm.App accepts a .csproj project file as an argument. First, the specified project is built. Then the emitted assembly is loaded and all its types are checked for prerequisites (no nested classes, no properties - see above for full list of limitations). If all checks are passed, source files are parsed and converted to JavaScript. Type information is used for identifier resolution. If the translator meets some unsupported syntax it will raise an exception.

The status of the project

The project is a kind of a spike solution. It’s coded accurately by using the TDD methodology, but it hasn’t been used in any production environment. So I won’t surprise a lot if you find show-stoppers it in. Please inform me about any misbehavior you notice.

Feel free to use or fork this project for any purpose. It’s available under the terms of MIT license. NRefactory library distibutes under LGPL and Mono.Cecil under MIT/X11.

Thanks for reading :)

Есть один комментарий

  1. zproxy:

    Nice work!

    How is it going for jsm latly? :)

Ваш комментарий