News: Stay up to date

The Étoilé community is an active group of developers, designers, testers and users. New work is being done every day. Visit often to find out what we've been up to.


Clang: Objective-C and JavaScript

Posted on 2 April 2011 by David Chisnall

There haven't been any updates here for ages, so I first wanted to reassure everyone that we're not dead. I've been focussing a bit more on clang and the GNUstep Objective-C runtime than on Étoilé recently, in the run up to the LLVM 2.9 release. The majority of this has just been tidying and fixing some bugs in some corner cases.

I've also tidied up and simplified the GNU runtime code in clang. This doesn't impact the functionality (although the code is now simpler, so should be a tiny bit faster, although I doubt that the difference is noticeable), but it should make it easier for other people to get involved. I've also refactored some of the code so that we are using the same code paths as Apple, which means that any bugs are more likely to be found and fixed. The exception handling code, in particular, has gone from being about 300 lines of code specific to the GNU runtimes to being about half a dozen, calling a generic implementation that we share with the Apple Modern runtime.

Clang 2.9 And Objective-C++

Exception handling, by the way, is something that's been reworked in clang 2.9 and the upcoming libobjc2 release. We now have proper interoperability with Objective-C++. This means that:

  • C++ and Objective-C exceptions can correctly propagate through each others' stack frames.
  • C++ catch statements can catch Objective-C objects thrown with @throw
  • Objective-C @catch statements can now catch Objective-C objects thrown with throw

This is semantically equivalent to the Apple unified exception model, and does not break the ABI - existing C++ and Objective-C code will still work when linked with Objective-C++ code compiled with this model. To enable this, you must specify -fobjc-nonfragile-abi when compiling Objective-C++, or you will get the old GCC-compatible (i.e. completely broken in most cases) mode.

That's not really what I wanted to talk about today though. One thing that we've talked about a few times over the past few years is adding something to GNUstep that sends PostScript-like commands to a browser, to be drawn in a canvas tag, and gets events back.

The GTK guys have now added a similar shim, and we still haven't got around to it. Part of the reason why not, is that I'm still not convinced that it's a good idea (in spite of the fact that it was my idea to start with).

Good NeWS!

To understand my objection, it's worth going back almost three decades, to a time when different UNIX vendors had their own windowing systems. Two of the competing systems at the time were the X Windowing System, from MIT, and NeWS, from Sun. Both used a client-server abstraction, where applications communicated with a display server.

In the case of X, this communication was very simple. The clients sent drawing commands, and the server sent events, like mouse clicks or keyboard button presses. With NeWS, the client sent PostScript programs, which handled drawing and low-level event handling.

To understand the difference, compare how a button would be implemented in the two approaches. With X, the client would send commands to draw the button to the server. When the user clicked on the button, the server sent a message to the client and the client sent a reply including instruction for drawing a pressed button.

In the NeWS model, the client sent a PostScript program representing a button. This drew a button and registered then waited for events. When the user clicked on the button, this program drew a pressed button and sent a 'button pressed' message to the server.

On a single machine, or even a local network, there isn't much difference between these two models. There becomes a difference, however, when you start using larger networks, such as the Internet.

When I'm using a remote machine over the Internet, it's not uncommon to get round trip times of 200ms or more. This means, with the X model, and assuming an infinitely fast server, it takes 200ms between clicking on the button and seeing the effect. Not a huge amount of time, but a noticeable lag. This is even worse on mobile networks: with GPRS, 2 second round trip times are not uncommon.

In contrast, the NeWS model meant that the response was always immediate. The view objects ran on the local machine, only the models ran on the server. Network latency was largely hidden from the end user. This is apparent in things like tree views: in the NeWS model, you only need to go to the server if the view is informed that new items have been added to the model. You can always expand and collapse tree nodes on the local machine. With the X model, every click-redraw cycle needs to go via the network.

Reinventing NeWS

One of the advantages of web applications over remote X11 is that they can run some client-side JavaScript code, effectively reimplementing the NeWS model. A tree view in a web app probably caches some local data and uses JavaScript to expand and collapse nodes, only fetching data from the server the first time that it's displayed. This makes web apps a lot more responsive over high-latency links than remote X11.

Unfortunately, streaming canvas drawing commands brings us straight back to the X11 model. It has the advantage of being simple (we estimated it would be under 5000 lines of code to add this support to GNUstep), but it's not very sensible.

Ideally, what we'd like to do is move the view objects into the browser, and maybe some of the controller objects, but leave the models on the server. This is what NeWS did, but it had the disadvantage that you had to write the views in PostScript. The web has a similar disadvantage: you can write the server-side logic in any language, but you have to write the views in JavaScript. We have a load of view and controller classes written in Objective-C already, and we'd like to be able to use them in a web browser.

Compiling Objective-C to JavaScript

One of the really nice things about clang is that it is modular. We're using it as a compiler front end, generating LLVM bitcode, which LLVM then compiles to native code. We're also using it for syntax highlighting and code indexing. The clang abstract syntax tree (AST) is exposed via a plugin interface, so it's easy to use clang for other things.

This has been my latest project: a plugin that walks an Objective-C AST and emits JavaScript code, along with a smallish supporting library. You can find the code in svn, as usual, along with some test cases.

To give a quick example of what works already, here's one of my test cases:

int *array = malloc(1024);
float *alias;
alias = (float*)array;
alias[2] = 1;
array[1] = alias[2];
((id*)array)[12] = [NSObject new];

[((id*)alias)[12] alert];

The jsalert() function and the -alert method on NSObject are implemented in JavaScript (as is the NSObject class itself), and just calls the JavaScript alert() function to pop up a dialog, for testing whether the code actually worked. This example shows several features of the compiler:

  • Pointers
  • Aliasing memory allocations via pointer
  • Storing and retrieving object pointers in memory of a different type
  • Sending messages to Objective-C objects (and classes)

The core of the C language works - including things like arrays of structures, and structures containing unions containing pointers - and so does the core of Objective-C.

The biggest omission, which will probably never be fixed, is integer-to-pointer casts. You can cast pointers to integers, and you can do arithmetic on pointers, but if you try to dereference a pointer that was created by casting from an integer then you will get a run-time error. On the plus side, you do get array bounds checking and full garbage collection for free...

What's still to do? Currently, only the core of Objective-C works. Exceptions don't (although they're easy to add), and most binary and unary operations on declared properties are broken. The majority of the remaining work is in the support library. We need to add JavaScript implementations of the common C library functions and reimplementations of some of the core classes in terms of their JavaScript equivalents (NSArray, NSString, NSView, NSBezierPath). With these done, we should be able to use GNUstep classes that depend on them after a simple recompile.