Taking notes on this thread on how some decisions might need to be made. Interface logic is all pretty easy and working already. If you are extending an existing Ruby class or no class (Object) and include interfaces, the logic goes like this:
1. A new class is generated, with the superclass being whatever the Java-side class is for the Ruby-side superclass. For example, "class Foo; include Runnable; end" would have as its Java superclass org.jruby.RubyObject. "class Foo < String; include Runnable; end" would have as its Java superclass org.jruby.RubyString. This allows extending existing Ruby core types and adding an interface impl...something that was not possible before. 2. All interfaces included so far are tallied up and their methods aggregated by name. All methods get implemented (to appease Java type requirements for interface impls) so that they dispatch directly to the single named Ruby method. For example, if you have five overloads for "valueOf" they'll all dispatch to a single Ruby method named "valueOf" or "value_of" (or else to method_missing). 3. The resulting "real" class is set up as the backing store for the Ruby class, so that all instantiations now actually construct instances of that generated Java class. This allows both the Ruby world and the Java world to see exactly the same object. Class extension is more complicated... When a Ruby class extends a Java class, we ideally want it to still be an IRubyObject so it can pass through JRuby calls without a wrapper. But this requires implementing all the IRubyObject methods. I have partially solved this on my branch by having a new BasicObjectStub with simple versions of all the IRubyObject methods, and a BasicObjectStubGenerator that can create all the method bodies. The new class extension logic uses this to "fill in the blanks" so that the "real" class can actually extend the one you specified, but also implement IRubyObject and pass through JRuby successfully. This does increase the size of the class a good amount since the IRubyObject interface is pretty large, but those are the breaks. Until a miracle happens and we can eliminate IRubyObject in the call path, this is the most straightforward way. The trickier bits of extension are still unimplemented, however: ** Constructor sequences ** We would like our "initialize" to actually serve the role of the Java-land constructor, but that's tricky; the typical sequence for construction is to first allocate the object and then call initialize against it with initialization arguments. In Java, you never see the allocate phase, and the entire construction process passes through the constructor method. We need a way to unify the two construction paths in a reasonable way. We also need our constructor to store off the original Ruby instance and RubyClass the object should be associated with, while still allowing no-arg construction for reflective construction from Java. This may force the issue of moving Ruby to being classloader-global, so that any classes generated under a given JRuby instance will know automatically which Ruby instance they came from. ** Super and "special" calls ** Subclasses need to be able to call protected members of the superclass as well as invoke the superclass's constructor. For protected methods, we currently just set them "accessible" so they can be invoked from anywhere. It's not an ideal solution, however, since it doesn't work in secured environments and can be costly. We *could* generate bridge methods in the subclass for all protected methods from the superclass, but that could be too many when you consider deep hierarchies with lots of protected methods. We'll need to look into what Groovy does and see if we can do that, though I suspect they solve it by using setAccessible. The "special" calls I mean are superclass constructor calls, which are only callable from within the child class and potentially only from within the child class's constructor. I'm not sure there's any way to get around this, and I know that Groovy has had to deal with the same challenge since the actual logic for their dispatch may not happen in the actual constructor body. We'll need to do some experimentation here. ** Abstract methods ** Like for interfaces, we will need to automatically implement all abstract methods from the parent class to do dynamic calls. If we don't, it won't be possible to extend abstract superclasses using metaprogramming or method_missing. We may even want to consider doing this for *all* overridable methods from the superclass, so that any one of them can be dynamically replaced such that both the Java and Ruby worlds see it. We'd be the first and only JVM dynamic language to take things to this level; even Groovy ends up being mostly stuck with the original method definitions when called from Java. ** Better IRubyObject stubs ** The current stubs are pretty weak. They don't allow Ruby instance variables at all on concrete subclasses. They don't do anything with freezing. They punt on a number of things, and we'll probably want to improve them. In addition, there's going to be cases in current JRuby code where we assume IRubyObject instances always extend RubyBasicObject or RubyObject and cast unconditionally. We'll need to search those out and replace them with something more agnostic. ... That's about all I have at the moment. On Wed, Dec 16, 2009 at 12:28 AM, Charles Oliver Nutter <[email protected]> wrote: > I'm working on a new branch of code ("newji" on the kenai repository) > that has started to add the following features: > > * Ruby classes implementing a Java interface will actually be instance > of a *real* Java class created on first instantiation. No more > two-headed object and chicken/egg initialization hassles. > * Ruby classes extending Java classes will *actually* extend those > classes and be *real* instances of the resulting class. No more > two-headed... > > This is part of a first effort to unify all the various > class-generation schemes we have, from the standard JI (generating > little stub classes and wrapping them) to ruby2java (generate real > classes ahead-of-time that dynamically invoke via Ruby classes) to > become_java! (generate real classes at runtime). The ultimate goal of > this is to finally make Ruby's type hierarchy fit correctly into > Java's type hierarchy, to bridge the remaining integration gap with > languages like Scala and Groovy. > > This first step is already working very well for interface > implementation. Check out this gist: > > http://gist.github.com/257634 > > This shows a Foo class that implements Runnable. When instantiated, > the instance of Foo is actually *physically* an instance of the > generated Java class. There's no wrapping layers at all, and the > object can be reflectively manipulated. > > The extension logic is shown there as well, but its not as > full-featured as the existing (extremely complex) logic. However > filling in the blanks will not be particularly difficult, and that's > what I'll be working on the remainder of this week. Next steps will be > to work out how to get "initialize" and Java construction to cooperate > nicely and adding back the features that the current class-extension > logic does (like implementing abstract methods for you). > > Once the basic logic for those two halves of Java integration code > generation are working as well as before, all the become_java! logic > will be added. This will essentially merge become_java into normal > Java integration, allowing you to add annotations, specify signatures, > and so on. > > The final, and most complex step, will be to get this all to work > ahead of time as well. This will require much more work and probably > will not go into this upcoming release. Basically we need a way to > determine statically) or through a minimal load process, like > ruby2java does) what classes to generate, what classes they extend and > interfaces the implement, and all their methods and annotations. It's > not so much a code-generation problem as a Ruby DSL/parsing/compiler > problem. But it's solvable, like all things. > > I'm starting to get very excited about this work. A change this major > would start to warrant a JRuby 2.0, with one other 2.0 piece being the > new interpreter/compiler. I'm starting to think 2010 is the year > JRuby's finally going to meet all the loftiest goals we've set for > it... > > - Charlie > --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email
