Closures in JRuby

Back in Chapter 2 I showed how JRuby executes a simple “puts 2+2” script. Now let’s take a look at how JRuby executes some Ruby code that is just a bit more complex, for example:


10.times do
  str = "The quick brown fox jumps over the lazy dog."
  puts str
end

Here’s a conceptual diagram showing how JRuby will execute this script:

image

Let’s walk through what’s going on in this diagram:

The important detail to learn here is that JRuby's byte code compiler generates a separate Java method for each block or other scope in my Ruby script. Also note how JRuby passes control back and forth between the compiled version of my Ruby script and the Java classes that implement the Ruby’s built in classes such as Fixnum.

Now let’s take a second example that uses a closure:

str = "The quick brown fox"
10.times do
  str2 = "jumps over the lazy dog."
  puts "#{str} #{str2}"
end

Here the block’s code refers to the str variable in the parent scope. JRuby compiles this script in same way as the previous example, generating a Java class that contains three methods. But how does JRuby allow the block to access the str from the parent scope? Does it use a DFP pointer and dynamic variable access like MRI does?

The answer is no. One of the important differences between JRuby and MRI is that JRuby does not use the JVM stack to keep track of different scopes or closure environments in your Ruby program, in the same way that MRI Ruby uses the YARV stack. There is no equivalent to the Dynamic Frame Pointer in JRuby. Instead, JRuby implements the same behavior using a series of Java classes, most importantly one called DynamicScope. When you reference variables from a different scope, i.e. when you use closures, JRuby saves the referenced variables inside a DynamicScope object. (Actually, there are a series of different Java classes that share DynamicScope as a common superclass that hold your closure variables.) In this example, JRuby saves my str variable inside a DynamicScope like this:

image

Later when JRuby executes the block, it creates a second DynamicScope object that refers to the parent scope like this:

image

Each DynamicScope object contains a parent pointer that indicates which other dynamic scope is the enclosing or parent scope for this closure. This is JRuby’s implementation of dynamic variable access. By iterating over these parent pointers, the JRuby Java code can get or set a variable that is present in a parent scope, or in the referencing environment of the closure.