WeakRef implementation for JRuby

Since this came up a few times, here’s an example of implementing
WeakRef in JRuby, using Java code. Obviously most of the heavy lifting
is done by Java’s WeakReference implementation.

I post this because I’m interested in hearing how this could be made
easier. In my eyes, this is pretty clean, and far simpler to create a
JRuby extension than a equivalent C extension…but I’m a Java guy. I’d
love to know how this can be improved. Thoughts?

<<JAVA_CODE

//// All Ruby objects extend RubyObject in JRuby right now. That may
change in the future to support objects in other branches of the class
hierarchy.

public class WeakRef extends RubyObject {

//// The Java WeakReference object, pointing at a ruby object

 private WeakReference<IRubyObject> ref;

//// JRuby likes to have an allocator defined for all objects that have
their own custom Java types. Here, we create an allocator for WeakRef

 private static final ObjectAllocator WEAKREF_ALLOCATOR = new

ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new WeakRef(runtime, klazz);
}
};

//// A requireable library must just implement Library. Here, we provide
a static inner class to do that…then it’s trivial to wire this up as
an extension.
//// Note also this defines the WeakRef class as extending Delegator,
and defines an Exception type for RefError.

 public static class WeakRefLibrary implements Library {
     public void load(Ruby runtime) throws IOException {
         RubyKernel.require(runtime.getKernel(),

runtime.newString(“delegate”), Block.NULL_BLOCK);

         RubyClass delegatorClass =

(RubyClass)runtime.getClassFromPath(“Delegator”);
RubyClass weakrefClass = runtime.defineClass(“WeakRef”,
delegatorClass, WEAKREF_ALLOCATOR);

         weakrefClass.defineAnnotatedMethods(WeakRef.class);

         RubyClass referrorClass = runtime.defineClass("RefError",

runtime.getStandardError(), runtime.getStandardError().getAllocator());
}
}

//// Standard constructor, pass in the JRuby runtime and the Ruby class
this object has as its metaclass.

 public WeakRef(Ruby runtime, RubyClass klazz) {
     super(runtime, klazz);
 }

//// Here’s the JRuby annotation for binding a method. This makes the
Java method “getobj” bind to WeakRef as WeakRef#getobj

 @JRubyMethod(name = "__getobj__")
 public IRubyObject getobj() {
     IRubyObject obj = ref.get();

     if (obj == null) {
         // FIXME weakref.rb also does caller(2) here for the 

backtrace
throw newRefError(“Illegal Reference - probably recycled”);
}

     return obj;
 }

//// Another such binding for WeakRef::new.

 @JRubyMethod(name = "new", required = 1, meta = true)
 public static IRubyObject newInstance(IRubyObject clazz,

IRubyObject arg) {
WeakRef weakRef = (WeakRef)((RubyClass)clazz).allocate();

     weakRef.callInit(new IRubyObject[] {arg}, Block.NULL_BLOCK);

     return weakRef;
 }

//// And for WeakRef#initialize. Notice that it dispatches to the
“super” implementation of initialize, which will call
Delegator#initialize

 @JRubyMethod(name = "initialize", required = 1, frame = true,

visibility = Visibility.PRIVATE)
public IRubyObject initialize(IRubyObject obj) {
ref = new WeakReference(obj);

     return callSuper(getRuntime().getCurrentContext(), new

IRubyObject[] {obj}, Block.NULL_BLOCK);
}

//// A binding for the weakref_alive? method

 @JRubyMethod(name = "weakref_alive?")
 public IRubyObject weakref_alive_p() {
     return ref.get() != null ? getRuntime().getTrue() :

getRuntime().getFalse();
}

//// And a utility method for constructing throwable RefError instances

 private RaiseException newRefError(String message) {
     RubyException exception =

(RubyException)getRuntime().getClass(“RefError”).newInstance(
new IRubyObject[] {getRuntime().newString(message)},
Block.NULL_BLOCK);

     return new RaiseException(exception);
 }

}

JAVA_CODE

  • Charlie