Java NullPointerException raised inside Proc gets printed out even though it was rescued #1473

Closed
DavidEGrayson opened this Issue Feb 2, 2014 · 8 comments

3 participants

@DavidEGrayson

If a java.lang.NullPointerException is raised from inside a Proc, its stack trace gets printed out even if the exception is successfully rescued from Ruby. This might apply to other Java exceptions too. I would expect that these exceptions can be silently caught without printing stuff to the console; is that right? They can be silently caught if the Proc is not involved, so that makes me think this is a bug.

I was able to reproduce this problem in JRuby 1.7.10 on both Windows and Linux. Below is some shell output showing my version of JRuby and reproducing the problem:

$ jruby -v -e "proc{ raise java.lang.NullPointerException.new }.call rescue nil; puts 'hi'"
jruby 1.7.10 (1.9.3p392) 2014-01-09 c4ecd6b on Java HotSpot(TM) 64-Bit Server VM 1.7.0_45-b18 [Windows 8-amd64]
java.lang.NullPointerException
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
        at org.jruby.javasupport.JavaConstructor.newInstanceDirect(JavaConstructor.java:259)
        at org.jruby.java.invokers.ConstructorInvoker.call(ConstructorInvoker.java:79)
        at org.jruby.java.invokers.ConstructorInvoker.call(ConstructorInvoker.java:160)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:316)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:145)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:149)
        at org.jruby.java.proxies.ConcreteJavaProxy$2.call(ConcreteJavaProxy.java:48)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:316)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:145)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:149)
        at org.jruby.RubyClass.newInstance(RubyClass.java:797)
        at org.jruby.RubyClass$INVOKER$i$newInstance.call(RubyClass$INVOKER$i$newInstance.gen)
        at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodZeroOrNBlock.call(JavaMethod.java:280)
        at org.jruby.java.proxies.ConcreteJavaProxy$3.call(ConcreteJavaProxy.java:141)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:306)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:136)
        at ruby.__dash_e__.block_0$RUBY$__file__(-e:1)
        at ruby$__dash_e__$block_0$RUBY$__file__.call(ruby$__dash_e__$block_0$RUBY$__file__)
        at org.jruby.runtime.CompiledBlock19.yield(CompiledBlock19.java:159)
        at org.jruby.runtime.CompiledBlock19.call(CompiledBlock19.java:87)
        at org.jruby.runtime.Block.call(Block.java:101)
        at org.jruby.RubyProc.call(RubyProc.java:290)
        at org.jruby.RubyProc.call19(RubyProc.java:271)
        at org.jruby.RubyProc$INVOKER$i$0$0$call19.call(RubyProc$INVOKER$i$0$0$call19.gen)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:202)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:198)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:306)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:136)
        at ruby.__dash_e__.chained_0_rescue_1$RUBY$SYNTHETIC__file__(-e)
        at ruby.__dash_e__.__file__(-e:1)
        at ruby.__dash_e__.load(-e)
        at org.jruby.Ruby.runScript(Ruby.java:811)
        at org.jruby.Ruby.runScript(Ruby.java:804)
        at org.jruby.Ruby.runNormally(Ruby.java:673)
        at org.jruby.Ruby.runFromMain(Ruby.java:522)
        at org.jruby.Main.doRunFromMain(Main.java:395)
        at org.jruby.Main.internalRun(Main.java:290)
        at org.jruby.Main.run(Main.java:217)
        at org.jruby.Main.main(Main.java:197)
hi

You can see that the exception was successfully caught because the word "hi" got printed at the end. However, some part of the system still printed a stack trace to the console.

The reason I found this is because I am on the jruby-1_7 branch and have almost cleaned up all the warnings that get printed for me when running rake spec:ji:int. However, a big Java stack trace gets printed out when spec/java_integration/extensions/kernel_spec.rb runs.

@DavidEGrayson DavidEGrayson added a commit to DavidEGrayson/jruby that referenced this issue Feb 2, 2014
@DavidEGrayson DavidEGrayson kernel_spec.rb: Added a workaround for jruby#1473 so it does not prin…
…t a big stack trace while running. Now I can run spec:ji:int without any warnings or failures (but a lot of pending).
fd350b3
@BanzaiMan
JRuby Team member

Java exceptions are not inherited from the Exception class, so you'll have to rescue them separately.

$ jruby -e 'puts java.lang.NullPointerException.ancestors' 
Java::JavaLang::NullPointerException
Java::JavaLang::RuntimeException
Java::JavaLang::Exception
Java::JavaLang::Throwable
Java::JavaIo::Serializable
Java::JavaLang::Object
ConcreteJavaProxy
JavaProxy
JavaProxyMethods
Object
Kernel
BasicObject
$ cat foo.rb 
begin
  raise java.lang.NullPointerException.new
rescue java.lang.NullPointerException
  puts "success"
end
$ jruby foo.rb 
success
@BanzaiMan BanzaiMan closed this Feb 9, 2014
@BanzaiMan
JRuby Team member

I need to check the specs for their correctness.

@DavidEGrayson

This isn't a matter of rescuing the exception or not rescuing it. I was able to rescue the exception somehow and the proof that I rescued it is that JRuby printed out the word "hi" in my example.

The real issue here is that something is printing the exception and stack trace to the console, even though it was caught. That issue still exists, and I don't think it is addressed by your comment.

Here is some code that more clearly reproduces the issue I was trying to report:

begin
  proc { raise java.lang.NullPointerException.new }.call
rescue java.lang.NullPointerException
  puts 'hi'
end

Running this code with JRuby prints "hi" which means the exception was rescued but it also prints a stack trace.

Your comment brings up another issue, which is that the CallingJavaFromJRuby wiki page has a note on it that seems to be incorrect:

Note: Java exceptions do not inherit from ruby Exception, and therefore will not be caught by a standard Ruby global rescue.

The code I gave in my first post seems to prove that this comment is incorrect and that the Wiki page does not agree with the current behavior of JRuby. However, that is not what I was trying to report.

@BanzaiMan
JRuby Team member

I see that the stack trace output issue, and it seems indeed a bug.

My understanding about the Java exception was wrong. Thanks for pointing that out. I'll update the Wiki page.

@BanzaiMan BanzaiMan reopened this Feb 9, 2014
@enebo
JRuby Team member

I fixed an issue of Proc having an errant printStackTrace only for NullPointerException earlier this week. Are you sure this is still an issue?

@BanzaiMan
JRuby Team member

Hah. Good timing. It's fixed on master and 1.7.

@BanzaiMan BanzaiMan closed this Feb 9, 2014
@enebo
JRuby Team member

Yeah I was fixing issues with travis and at least I think got jruby-1_7 passing but this weird NPE printout during the run was bugging me (since it did not actually fail the build).

@enebo enebo added this to the JRuby 1.7.11 milestone Feb 13, 2014
@enebo
JRuby Team member

Putting milestone on this so we can show it has been fixed in release notes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment