Improve two-way integration of Java and Ruby exceptions #4781
For #4699, we removed the automatic initCause call for Java exceptions raised from Ruby. There were a few issues with that logic:
In light of these problems, I think we should not reinstate automatic cause-setting for Java exceptions, but we do need a way to provide a cause. There's a few ways this might be done...I try to describe some below.
RubyException could hold a reference to its "container" RaiseException, so it can be re-raised without making a new one.
When providing the Java cause, this exception could be used, either through a new JRuby-specific method:
ex.throwable # or ex.to_java(Throwable)
...or via some magic trickery during value conversion in JI, when we see a RubyException is being passed for a Throwable.
We have also discussed in the past building a parallel exception hierarchy for RaiseException. In this structure, all exception types that currently exist would still work, but for each Ruby exception there would be an equivalent Java exception of the same name. The two would be inseparable, like the proxy objects we use for JI. This would allow the Ruby exception to behave more naturally as a Java exception, and allow Java code to rescue Ruby exceptions by name (rather than the RaiseException dance we have today).
All this combined would improve our integration between Ruby and Java exceptions.
The text was updated successfully, but these errors were encountered:
I have long wanted to set up a parallel exception class hierarchy in Java to match the Ruby hierarchy, so you could rescue Ruby exceptions by type. This is the beginning of that work. See #4781
This change allow callers to skip the cast when calling toJava on a Ruby object. It was initially done to assist work on #4781 by allowing a simple "throw rubyException.toJava(Throwable.class). I tested binary compatibility in two ways: * A full recompile with all generification in place plus testing standard JRuby commands. This confirms that classes recompiled against the API but without other use of generics still function properly. * A partial recompile with only the generified classes rebuilt. This tests that classes not compiled against the API still function properly. Of course any external JRuby extensions (readline, openssl) will have been tested by running those standard commands (e.g. irb, gem install).
I have abandoned the old branch and have a new (working) branch in exceptions2.
The logic here works like this:
At the moment this does not introduce any new Throwable types to the system. I need to work on cleaning up initialization, especially wrt backtraces.
This work is the start of producing a Java-domain Throwable hierarchy that matches the Ruby hierarchy, so that Ruby exceptions can be caught using their actual Throwable type rather than using RaiseException and checking the exceptions type manually. A few changes of note: * RubyException has most logic moved up to new AbstractRubyException. * All RubyException now hold a reference to their RaiseException, created lazily once as needed. * Throw a Ruby exception using the form `throw ex.getRaiseException()`. * Provide construction logic specific to a given exception by overriding createRaiseException. * The nativeException parameter is eliminated from all exception construction and initialization paths. It was unused.
This forms the meat of wiring up the new parallel exception hierarchy. All public constructors of RaiseException are now deprecated and unused. RaiseException.from() forms take their place and know how to properly construct a Ruby exception and wrap it with an appropriate RaiseException subclass. Three native exceptions are added, mapping to the Ruby exception type of the same name: Exception, StandardError, and SignalException. One location in the code that caught RaiseException is now able to catch StandardError (RubyNumeric coerce logic). There are likely others. See #4781.
This includes everything in standard Ruby plus our additions but minus the errno classes. See #4781.
Ok, without any additional modification, we can now do the following to attach a Ruby exception as the cause for a Java exception:
We'd need to set up all Exception classes to report Throwable as their "native" Java class, but I think that's all. They'd be eligible for passing to methods that take Throwable, and the toJava logic I added will retrieve the same throwable every time.