I have just landed an early prototype of the compiler on trunk. I made a few decisions about it today:
- It will use my bytecode DSL "BiteScript", just like Duby does
- It will use the *runtime* definition of a class to generate the Java version
Here's an example:
# myruby.rb
require 'rbconfig'
class MyRubyClass
def helloWorld
puts "Hello from Ruby"
end
if Config::CONFIG['host_os'] =~ /mswin32/
def goodbyeWorld(a)
puts a
end
else
def nevermore(*a)
puts a
end
end
end
Here we have a class that defines two methods. The first, always defined, is helloWorld. The second is conditionally either goodbyeWorld or nevermore, based on whether we're on Windows. Yes, it's a contrived example...bear with me.
The compiler2 prototype can be invoked as follows (assuming bitescript is checked out into ../bitescript):
jruby -I ../bitescript/lib/ tool/compiler2.rb MyObject MyRubyClass myruby
A breakdown of these arguments is as follows:
- -I ../bitescript/lib includes bitescript
- tool/compiler2.rb is the compiler itself
- MyObject is the name we'd like the Java class to have
- MyRubyClass is the name of the Ruby class we want it to front
- myruby is the library we want it to require to load that class
Compiled from "MyObject.java.rb"
public class MyObject extends org.jruby.RubyObject{
static {};
public MyObject();
public org.jruby.runtime.builtin.IRubyObject helloWorld();
public org.jruby.runtime.builtin.IRubyObject nevermore(org.jruby.runtime.builtin.IRubyObject[]);
}
The first thing to notice is that the compiler has generated a method for nevermore, since I'm not on Windows. I believe this will be unique among dynamic languages on the JVM: we will make the *runtime* set of methods available through the Java type, not just the static set present at compile time.
Because there are no type signatures specified for MyRubyClass, all types have defaulted to IRubyObject. Type signature logic will come along shortly. And notice also this extends RubyObject; a limitation of the current setup is that you won't be able to use compiler2 to create subclasses. That will come later.
Once you've run this, you've got a MyObject that can be instantiated and used directly. Behind the scenes, it uses a global JRuby instance, so JRuby's still there and you still need it in classpath, but you won't have to instantiate a runtime, pass it around, and so on. It should make integrating JRuby into Java frameworks that want a real class much easier.
So, thoughts? Questions? Have a look at the code under tool/compiler2.rb in JRuby's repository. The entire compiler is so far only 78 lines of Ruby code.
This is a great step towards being able to use JRuby with those pesky Java libs that require you to just specify the class to be loaded as a string or in some config file. I like the fact that you can use the class's static initializer to pull in the JRuby runtime instance, that makes these classes usable in all sorts of places where you can't control the construction of your object. Bravo!
ReplyDeleteDavid: Yeah, a perfect example of that is serialization. There's no reason the compiler2 output couldn't be serialized and deserialized with normal Java serialization, since there's a no-arg constructor and all the state would be transient. We just need a bit of logic to marshal the Ruby innards on the way out and unmarshal them on the way in.
ReplyDeleteHi,
ReplyDeleteIt very interesting add !! will it be possible to migrate a ruby object to a java one at runtime ?
Thanks in advance for your answer ... and again many thanks for this great work
Adam
This is amazing and brilliant. Can this compiler do the AOT compilation as well? If so how would create a jar from the compiled code?
ReplyDeleteThe efficiency this adds to calling Jruby from Java is just incredible.
Support for AOT compilation and packaging of JRuby code in Jars would be great too.
ReplyDelete