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.

3 Responses to “Objects, Classes, and JRuby Internals”

  1. John Hume

    In getSingletonClass, why does a RubyObject have to check the “metaClass’s” __attached__ instance variable to see if it’s attached to itself? What else could it be attached to?

  2. lopex

    Hi, we’ve been performing some major refactorings in JRuby code base lately. The trunk doesn’t use non-lookupable instance variables for __attached__, __classpath__ and __classid__ anymore, this allowed us to lazily create instance variable containers. The __attached__ is now a plain java field. Class construction path has also been heavily refactored for performance and no new Class class allocator instances are created now per new user class.

    John Hume: the __attached__ thing might be also a null, in general it acts as a back pointer in singleton class chain.

  3. pfarley

    Hi John,

    This gets a bit tricky.

    Suppose you add instance specific behavior to the pirate object. There now exists a Singelton class ‘pirate. A legitimate question is, “What is the class of ‘pirate?” If the world were sane the answer would be ”pirate, or Singleton class of the Singleton class of pirate. We know that ‘pirate’s super pointer points to Pirate, and, since ”pirate is a Metaclass (the class of the ‘pirate class), it’s super pointer should point to the Metaclass of ‘pirate’s super class. That would be ‘Pirate or Metaclass of Pirate. Another way to say this is, ” ‘pirate subclasses Pirate so ”pirate subclass ‘Pirate”. The __attached__ hack comes in because Ruby takes a shortcut. When it creates ‘pirate instead of setting it’s metaClass (or klass in MRI) to ”pirate, it sets it directly to ‘Pirate. This way method dispatch continues to work normally, and messages sent directly to ‘pirate (class methods) are matched against methods found in ‘Pirate. If you decide to define instance specific behavior on ‘pirate, the interpreter will grab the class of ‘pirate and find a Singleton class there, ‘Pirate, that is not attached to ‘pirate (it’s attached to the Pirate class), so the interpreter knows to create a new Metaclass, ”pirate, on the fly whose super pointer is ‘Pirate, and all is well in the world. The madness pretty much stops there. Ruby goes circular at that point, with the class of ”pirate being ”pirate. This happens in some code I elided from the makeMetaClass source in my post. It’s all pretty interesting, but so fringe that I don’t think it’s really worth people cooking their noodles over.

    //RubyObject:217
    if (this instanceof RubyClass && isSingleton()) { // could be pulled down to RubyClass in future
    klass.setMetaClass(klass);
    klass.setSuperClass(((RubyClass)this).getSuperClass().getRealClass().getMetaClass());
    } else {
    klass.setMetaClass(superClass.getRealClass().getMetaClass());
    }

    Hi Lopex,

    Will definitely spend some time looking about in trunk. Thanks for reading the blog and all your JRuby work. Can you tell me under what circumstances __attached__ is null?