Inside SWT

Wednesday, November 07, 2007

Hacking at Apple

Silenio and I are at Apple this week. We were invited to take a course which turned out to be a cocoa hack-a-thon. It's a good deal for me because I get to code and get away from distractions at IBM.

As many of you know, cocoa is Objective-C and SWT is programmed from top to bottom in Java (otherwise, we couldn't possibly develop or maintain the toolkit). So the first order of business was to figure out how to call Objective-C from Java. We needed to convert this:

[object message: 12];

into Java.

Obviously, we could have a billion natives, one for each message, but this would be too crazy, even for us. So we got the compiler to dump the assembler to see what was going on. It turns out that everything is a call to objc_msgSend() so we typed these guys in and were on our way.

On Monday, Scott Kovatch (my friend at Apple) said, "Hey, we've got a description of all this stuff in XML". He showed us where it was and by the start of the next day, Silenio had all of the selectors and classes that we needed. So instead of typing:

OS.objc_msgSend(id, OS.sel_message, 12);

we were typing:

object.message(12);

Everyone else in the room thinks we're insane.

Steve

19 Comments:

  • Great stuff. Seems like there's life in Eclipse on the Mac yet :-)

    By Blogger AlBlue, at 4:27 AM  

  • When your there, please also think of http://bugs.eclipse.org/68111
    Any pointers and quick fixes coming from some internal aopple xml document would be great!

    By Blogger Maarten Meijer, at 5:43 AM  

  • I assume that XML description is part of BridgeSupport. What a great idea. Imagine if everyone did that.

    By Blogger Patrick Mueller, at 8:39 AM  

  • So we got the compiler to dump the assembler to see what was going on. It turns out that everything is a call to objc_msgSend() so we typed these guys in and were on our way.
    That seems to be a rather difficult and incomplete way to find out what can be learned from the runtime reference or the language reference.

    I guess you did call it "hacking," though...

    By Anonymous Anonymous, at 9:07 AM  

  • "Everyone else in the room thinks we're insane."

    They might be right!

    By Anonymous Anonymous, at 9:35 AM  

  • Hey anon-y-moose,

    Yes, I am aware of the spec and we are using it. The main idea was to make sure that the code we wrote, when JIT'd from Java would be the same speed as code fully compiled by Objective-C.

    By Blogger Steve, at 10:11 AM  

  • Does this mean that an SWT port to Cocoa is in consideration? (*hope* *hope*)

    You dumped the raw assembly to get the message codes?!?! lol

    By Blogger Daniel Spiewak, at 2:19 PM  

  • I don't think you need to reverse engineer to get the IDs. From the Objective-C FAQ:

    5.3 How do I know the SEL of a given method ?

    If the name of the method is known at compile time, use @selector :

    [myObject perform:@selector(close)];

    At runtime, you can lookup the selector by a runtime function that takes the name of the message as argument, as in :

    SEL mySel = selUid(name); // for Stepstone
    SEL mySel = sel_getUid(name); // for Apple
    SEL mySel = sel_get_any_uid(name); // for GNU Objective C
    SEL mySel = selUid(name); // for POC

    Alex

    By Blogger AlBlue, at 6:22 PM  

  • Generic disparaging comment to show my own feelings of superiority and knowledge of Objective-C.

    By Anonymous Anonymous, at 12:24 AM  

  • It wasn't meant that way. I thought it might be genuinely useful to save time reverse engineering code for other Cocoa objects in the future.

    By Blogger AlBlue, at 4:30 AM  

  • Hi Steve,

    I don't get this: "SWT is programmed from top to bottom in Java"

    Doesn't SWT have native underpinnings (those DLLs, etc. that are distributed with SWT...)?

    By Anonymous Anonymous, at 8:36 PM  

  • Anonymous,

    A bit of both. SWT exposes the underlying OS's functions directly as Java/JNI methods. So for every OS native, there's a corresponding Java call.

    The majority of SWT is then Java classes that invoke those OS native functions via JNI. So all the real work is implemented in Java, whilst the native code exists purely as a way to translate Java calls into OS calls.

    It means that bugfixing/development/patching is all done at the Java level. In the case of Apple, all of the Cocoa/AppKit classes are exposed as corresponding Java classes, and then the SWT code invokes/uses those.

    Alex

    By Blogger AlBlue, at 3:52 AM  

  • Elaborating on Alex's comments...

    SWT's native code design is very similar to the design pattern imposed by the JNA project (not related to SWT in any way). They have some examples on their project page of how it works in code. https://jna.dev.java.net

    In the case of SWT, all of the native methods required for functionality are peered using public static native methods in org.eclipse.swt.internal.win32.OS (internal.cocoa for the Cocoa port).

    By Blogger Daniel Spiewak, at 9:43 AM  

  • To be clear, we do not expose all of the functionality on the underlying operating system. Right now, for the cocoa port, there are many more methods and classes than are used by SWT. We will trim this before we ship.

    By Blogger Steve, at 12:26 PM  

  • Glad someone is finally moving in that direction. I started my own port for which I had all the dynamic libraries building properly the same way the other ports work. I also got to the point where I was able to play with simple shells from Java. It is not really difficult. I posted something a while back on the swt mailing list. my thought was (still is) that the difficulty is more the tedium of gathering the selectors that make sense for SWT. I started that the same way things were handled in the little bit of Obj-C present in the carbon port (for the browser). But it looked stupid to do that manually. So I also followed the track of CamelBones and BridgeSupport (Python Obj-C). Basically I have a hunch this is how apple's java cocoa bridge was done. So I landed back into the SWT COM bridge as a model for what to do/not do (not a complete Java-Cocoa bridge, but more than the Cocoa code in the SWT-Carbon tree).

    fortunately, the Obj-c runtime is quite simple to emulate, and I think it makes sense to provide some java classes that hide the msg_send details, in the same fashion that it was done for SWT-COM.

    Like I said in my previous message on the mailing list, I found that the main difficulty is the binding of threads with the NSxxx way of dealing with events.

    By Blogger L, at 11:40 PM  

  • Well, the work in progress port is all there, check it out.

    Before we ship (whenever that is), we'll delete all the cocoa classes and methods that we did not use for space reasons.

    By Blogger Steve, at 8:43 AM  

  • Is there a link somewhere explaining how the Java / Objective-C / COCOA integration is done?
    Let's say I'd want to wrap ObjC / COCOA calls in a language like Java, how would I go with it?

    By Blogger Guillaume Laforge, at 4:18 AM  

  • Actually, I meant in a language like "Groovy" :-)

    I've done an integration with Groovy which wraps calls to COM / ActiveX objects on Windows, so that you can easily call native applications using a syntax similar to VBScript or JScript.

    See Scriptom:
    http://groovy.codehaus.org/COM+Scripting

    I would like to the same thing to call COCOA apps from within Groovy.

    By Blogger Guillaume Laforge, at 4:26 AM  

  • Do some googling for the Objective-C / Java bridge. It's a bit deprecated (yes, I know that's debatable), but you should be able to get stuff done using it. There are a few gotchas like arrays and booleans, but on the whole things work fairly well.

    Alternatively (and potentially more easily), you can just use JNA to control C code which then calls up into Objective-C directly. Minus the JNA convenience layer, this is exactly how the objective-c / java bridge works.

    By OpenID Daniel, at 2:00 PM  

Post a Comment

<< Home