Question

When I compile a ruby file to a java class using jrubyc, I get different output when compiling with just jrubyc and with jrubyc --java (to generate the java file) and just javac. Why?

Example:

First method:

jrubyc --java myscript.rb
javac -cp .:./jruby-complete.jar myscript.java

Second Method:

jrubyc myscript.rb

I'd expect the generated classes to be exactly the same, but they're not. What's jrubyc doing under the covers?

Thanks!

Was it helpful?

Solution

jrubyc myscript.rb compiles the Ruby file for JRuby consumption and cannot be used from Java, hence the name AOT. The code used to compile it is the normal JRuby compiler that's used to transform to bytecode. You can only use the resulting myscript.class in a JRuby script, using for instance require 'myscript'. When using javap:

ubuntu@ubuntu:/tmp$ javap myscript
Compiled from "myscript.rb"
public class myscript extends org.jruby.ast.executable.AbstractScript {
  public myscript();
  public static org.jruby.runtime.builtin.IRubyObject __file__(myscript, org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.builtin.IRubyObject[], org.jruby.runtime.Block);
  public org.jruby.runtime.builtin.IRubyObject __file__(org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.builtin.IRubyObject[], org.jruby.runtime.Block);
  public static org.jruby.runtime.builtin.IRubyObject class_0$RUBY$MyScript(myscript, org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.Block);
  public static org.jruby.runtime.builtin.IRubyObject method__1$RUBY$run(myscript, org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.Block);
  public static org.jruby.runtime.builtin.IRubyObject method__1$RUBY$run(myscript, org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.builtin.IRubyObject[], org.jruby.runtime.Block);
  public static org.jruby.runtime.builtin.IRubyObject class_0$RUBY$MyScript(myscript, org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, org.jruby.runtime.builtin.IRubyObject[], org.jruby.runtime.Block);
  public org.jruby.runtime.builtin.IRubyObject load(org.jruby.runtime.ThreadContext, org.jruby.runtime.builtin.IRubyObject, boolean);
  public static void main(java.lang.String[]);
}

we see the extended class inherits org.jruby.ast.executable.AbstractScript and defines lots of internal methods, so it is clear this code is for JRuby's AST use.

That's why jrubyc provides two extra options: --java and --javac: the first one generates the Java source code that wraps the code to the JRuby script using ScriptingContainer, just as you normally would with the original script; the second one produces directly the compiled Java class directly. This code uses specific Java generator code that uses directives such as java_signature to give the Java methods the correct signatures as expected by Java. When using javap again:

ubuntu@ubuntu:/tmp$ jrubyc --javac myscript.rb
ubuntu@ubuntu:/tmp$ javap MyScript
Compiled from "MyScript.java"
public class MyScript extends org.jruby.RubyObject {
  public static org.jruby.runtime.builtin.IRubyObject __allocate__(org.jruby.Ruby, org.jruby.RubyClass);
  public MyScript();
  public java.lang.Object run();
  static {};
}

the class starts with an uppercase M, and inherits RubyObject. Methods defined in the class will be exposed for Java consumption.

Using JRuby: Bringing Ruby to Java has a good description of these two forms in Chapter 4, The JRuby Compiler.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top