Objects, Classes, and JRuby Internals
A walk through of the JRuby implementation wraps up this series on Ruby internals and Singleton classes.
JRuby is a Java implementation of the Ruby interpreter. That is to say, that one can write plain ol’ Ruby files and hand them off to the JRuby interpreter for execution in the same way that they can be executed via Matz’s Ruby interpreter (MRI). From a Ruby object model perspective, JRuby isn’t doing anything all that different from MRI. Specifically, a Ruby class Pirate, when interpreted by JRuby ,is NOT transformed into a Java Pirate class. Rather, Pirate is represented by an instance of RubyClass in much the same way that MRI uses structs.
JRuby itself is very important, and gaining a familiarity with it’s implementation is a good idea, independent of MRI. That said, the parallels between the two interpreters are a boon for any developer in acquiring an advanced understanding of Ruby internals. Inspection of JRuby source can give insight and perspective into how MRI works. This is particularly useful to developers more comfortable in Java than C.
In JRuby, Ruby objects are represented by instances of the RubyObject class…
The data members on RubyObject should look very familiar, they map one to one to RObject and RBasic in MRI. There are a few interesting differences.
The Ruby class of an object, stored as klass in MRI, is stored in a data member named metaClass. This can be very confusing. The value stored in metaClass is not necessarily a Metaclass or even a Singleton class in the Ruby sense. It is simply the class of the object. If the object is an instance of Class, than metaClass does indeed store a Metaclass, if it is a “normal” object, it stores either a “normal” Ruby class or a Singleton class, depending on whether or not instance specific behavior has been defined.
flags also work a bit differently in JRuby. They are used to store interesting state for an object (is it frozen? tainted?) as in MRI, but certain information that would be stored in flags in MRI is simply defined as methods in JRuby. JRuby has the advantage of being able to subclass RubyObject, making it possible to represent this information differently by overriding the methods in subclasses. For example…
Like MRI, JRuby hides the actual class of an object by returning a “real class”. The differences are just OO implementation details. Rather than inspect the class itself, RubyObject asks it’s class for it’s “real class”…
If the the object’s metaClass is storing a Singleton class, represented by the JRuby MetaClass class, the class delegates to it’s super…
Eventually the interpreter hits upon a “real class” represented by an instance of RubyClass, which returns itself…
In both flavors of Ruby it’s convenient to say, “the Singleton class is created when instance specific behavior is added to an object”. A more accurate statement is, “a Singleton class is created the first time an attempt is made to access the Singleton class of an object”. In practice the two are often equivalent. Once again JRuby’s behavior parallels MRI…
Here, the interpreter is checking the Java object stored in metaClass to see if it is a Singleton class associated with the object. If so it returns the object stored there, otherwise it generates a new Singleton class by calling makeMetaClass. As in MRI, getSingletonClass passes it’s current class in for use as the superclass of the new Singleton class. The second parameter, is used for lexical scoping and does not play directly into the object model. For clarity I’ve omitted some of the makeMetaClass source …
makeMetaClass parallels MRI exactly. The JRuby interpreter creates a new class and assigns it to the metaClass replacing the current class of the object.
In JRuby, classes are created in the static newClass method. As all classes inherit directly from another class or the Object class, the interpreter calls newSubClass on the “superclass to be” to actually generate the class…
Importantly, makeMetaClass is called immediately after the new class’s instantiation to create the Metaclass of the new class. As this method is being called from the new class’s super class, the first parameter passed to makeMetaClass, getMetaClass refers to the class of the new class’s super class. In other words, the “The superclass of the Metaclass is the Metaclass of the superclass.”
I encourage you to crack open the JRuby source yourself and take a look around. This post is accurate as of JRuby 1.1. The JRuby folks have lots of big and exciting changes coming down the pipeline with regards to the internal representation of classes. Thanks to Ola Bini for being good enough to answer some of my JRuby questions and to the entire JRuby team for gifting us with their work.