Thursday, March 09, 2006

Progress Update March 9

It's been a busy few days. We've made more good progress, so I figured an update was due.

Last Class

Tom discovered over the weekend that we had a problem setting the "last class" in the interpreter. This interfered with "super" calls working correctly.

The "last class" is used to point at the appropriate class hierarchy for the currently-executing code. We were setting "last class" to the actual class or module the current object was an instance of. This is correct for classes, but it was completely wrong for modules. In Ruby (both C and J), modules are internally inserted into the inheritance chain using module "wrappers". These wrappers become an implicit superclass of the class doing the include, and the wrapper's superclass points at the class's original superclass. So in the following example...

module X; end
class Y; end
class Z < Y; include X; end


..the explicit class hierarchy shows Z extending Y, while internally Z's superclass is a wrapper for X, whose superclass is then Y.

Having "last class" set appropriately in the interpreter is important because super calls must always be able to traverse the appropriate hierarchy. If, for example, you created a module-based "initialize" method (which some apps do to mix-in initialization behavior), and that initialize method called super, it must go to the appropriate superclass's initialize. Since modules do not have an initialize (and are uninstantiable), our original "last class" pointer at the module itself failed miserably. The initialize method was called correctly on the module, but super pointed at nothing.

Once Tom had figured out how we ought to fix this, I whipped up a patch. We modified the method-calling pipeline to send the appropriate "last class" through to execution. This allows module methods to now run within the context of the includer's hierarchy, rather than in their own hierarchies. With that fixed, super calls are now working correctly.

Array#unshift

Array#unshift now accepts an empty arg list. This is perhaps a Ruby 1.8.4 change. We had been forcing it to be at least one argument, as is documented in pickaxe. I noticed this problem while re-testing IRB with the "last class" fixes.

eval + Binding fixes

Binding is a fairly recent addition to JRuby. We had not ever supported binding up until a few weeks ago, which killed apps like IRB and Rails in the cradle. Adding it opened up a world of opportunity.

However, yesterday I discovered that code eval'ed with a Binding was not getting "self" correctly. Throughout the interpreter, there's one universal constant: "self" points to the current object. In a pure OO language like Ruby, all code is executed in the context of an object. Code at the top level executes within the context of the top-level Object instance, instance methods execute within the object they are called against, and so on.

When calling eval, you can choose to specify a binding or not. eval("code") evaluates the code in the current object (i.e. the current "self"), as well as within the current call frame, scope, class, etc. eval("code", some_binding) evaluates the code within another context entirely, with a different "self", frame, and friends.

Our eval, though properly setting up the bound context's frame and friends, was not correctly setting self. It was always making self the current object. I fixed it to set self to the correct object when called with a binding, and it appears to be working correctly.

IRB

With my update to Ruby 1.8.4 code and the "last class" fixes, IRB suddenly stopped working. Bummer. To make matters worse it started failing late last night, and I couldn't find all the issues.

However, on the bus ride to work today, I fixed both the eval and Array problems that were preventing IRB from working. Starting up IRB again I received a lovely surprise:

C:\JRubyWork\jruby>jirb
irb(main):001:0>


It is the good old IRB prompt, rather than the hideous IRB::Workspace::Nonsense!

The "self" issue prevented IRB from running at all with the "last class" fixes in place, and was the cause of two other issues with IRB: the need for --single-irb mode, and the ugly prompt.

With the eval issue fixed, IRB now runs without the need for --single-irb mode, and I have updated our "jirb" scripts to reflect this. It also runs with the appropriate prompt. This will make me much happier and our future IRB demos much prettier.

Rails

My goodness Rails is a gigantic thing.

We are continuing to make good progress on Rails. With all the above fixes, the generate script now runs through to initialization of the Routing subsystem. There it fails with an argument error that could be an interpreter or API issue.

I have also ventured a bit beyond this--by commenting routing out--and ran into a Ruby syntax we do not yet support:

x = {}
[1].each {|x[:foo]|}

Ruby at some point (probably in 1.8) added the ability to specify an array or hash index as the target for a block argument. While I can see the usefulness of this shortcut (rather than {|v| x[:foo] = v}) I must say this syntactic sugar is a bit sweet for my tastes. At any rate, it's there and we need to support it.

Rails so far only uses this syntax when printing out usage for the generate script. I modified that script to use the longhand version, and it was able to continue.

With routing commented out and the block arg syntax modified, the generate script ran to completion. We're getting closer!

Executing the generate script with a specific generator caused some other issues, whereby JRuby sees a yield without seeing a block to yield to. It's not functional yet, but it's moving right along.

Tom is also working on the other end of things, running Rails in "CGI mode" to test execution of an actual Rails request. At this point he's stuck on the same argument issue that's preventing routing from starting up, so that's what we'll work on next.

JavaOne 2006

As most of you will know, we will be presenting JRuby at JavaOne this year. We have almost finished our slides and will be sending them to Sun tomorrow. We obviously can't show you the slides themselves (since we want you to come see them in person) but we do hope to do most of the following five demonstrations:

  • Interactive JRuby with IRB
  • JDBC in Ruby
  • Swing in Ruby
  • Spring in Ruby
  • Rails
Obviously the Rails demo will only happen if there's something to show...but we're banking on having something working by then. We may have to drop one of the others for time, but I hope to touch on them all. We hope to see you all at JavaOne!