Clojure

Better handling of exceptions with empty stack traces

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Minor Minor
  • Resolution: Completed
  • Affects Version/s: Release 1.5
  • Fix Version/s: Release 1.6
  • Component/s: None
  • Labels:
  • Patch:
    Code and Test
  • Approval:
    Ok

Description

REPL session demonstrating clojure.stacktrace/print-stack-trace and clojure.test/file-and-line throwing exceptions when given Throwable with an empty stack trace:

user=> (require '[clojure.stacktrace :as s])
nil
user=> (def empty-stack (into-array (Class/forName "java.lang.StackTraceElement") []))
#'user/empty-stack
user=> (def t (doto (Throwable.) (.setStackTrace empty-stack)))
#'user/t
user=> (def msg (with-out-str (s/print-stack-trace t)))

NullPointerException   clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
user=> msg
#<Unbound Unbound: #'user/msg>
user=> (require 'clojure.test)
nil
user=> (def m1 (#'clojure.test/file-and-line t 0))

ArrayIndexOutOfBoundsException   java.lang.reflect.Array.get (Array.java:-2)
user=> m1
#<Unbound Unbound: #'user/m1>

I have seen this cause confusing output when exceptions with empty stack traces are thrown while running tests on a project. According to the Java docs for Throwable, it is permissible for getStackTrace to do this:

http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29

Approach:

I found all places in the Clojure code that call getStackTrace. Among them, two did not handle an empty stack trace correctly.

Output of tests above with this patch applied:

...

user=> (def msg (with-out-str (s/print-stack-trace t)))
#'user/msg
user=> (print msg)
java.lang.Exception: null
 at [empty stack trace]
nil

...

user=> (def m1 (#'clojure.test/file-and-line t 0))
#'user/m1
user=> m1
{:line nil, :file nil}

Patch: clj-1102-improve-empty-stack-trace-handling-v2.diff

Screened by: Alex Miller

Activity

Andy Fingerhut made changes -
Field Original Value New Value
Attachment clj-1102-improve-empty-stack-trace-handling-v1.txt [ 11662 ]
Andy Fingerhut made changes -
Patch Code [ 10001 ]
Timothy Baldridge made changes -
Assignee Timothy Baldridge [ halgari ]
Timothy Baldridge made changes -
Assignee Timothy Baldridge [ halgari ]
Timothy Baldridge made changes -
Approval Vetted [ 10003 ]
Andy Fingerhut made changes -
Description I don't know what I did to cause some exceptions to be thrown while running Clojure tests that return a length 0 array from (.getStackTrace throwable), but according to the Java docs this is legal. I searched all places in the Clojure code that call .getStackTrace and found two that don't handle this correctly, one of which causes an ArrayOutOfBoundsException (that is the one I found during my testing). Problem:

I do not recall how I caused this issue to happen. I have encountered it while building and testing a locally-modified version of Clojure. An exception was thrown. There began the process of starting to print details about the exception, and then another exception occurred during that printing because there was an attempt to access index 0 of a length 0 array returned from (.getStackTrace throwable). According to the Java docs for Throwable, it is possible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29

Patch: clj-1102-improve-empty-stack-trace-handling-v1.txt

Approach:

I found all places in the Clojure code that call .getStackTrace. Among them, two did not handle this case correctly, one of which causes an ArrayOutOfBoundsException (that is the one I found during my testing).

Regarding adding a test for this, it is easy to create an exception, set its stack trace to a length 0 array with setStackTrace, and throw it. Not sure how to take that and make a test that verifies that the new corrected clojure.test/file-and-line does not throw an exception.
Andy Fingerhut made changes -
Patch Code [ 10001 ] Code and Test [ 10002 ]
Description Problem:

I do not recall how I caused this issue to happen. I have encountered it while building and testing a locally-modified version of Clojure. An exception was thrown. There began the process of starting to print details about the exception, and then another exception occurred during that printing because there was an attempt to access index 0 of a length 0 array returned from (.getStackTrace throwable). According to the Java docs for Throwable, it is possible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29

Patch: clj-1102-improve-empty-stack-trace-handling-v1.txt

Approach:

I found all places in the Clojure code that call .getStackTrace. Among them, two did not handle this case correctly, one of which causes an ArrayOutOfBoundsException (that is the one I found during my testing).

Regarding adding a test for this, it is easy to create an exception, set its stack trace to a length 0 array with setStackTrace, and throw it. Not sure how to take that and make a test that verifies that the new corrected clojure.test/file-and-line does not throw an exception.
REPL session demonstrating clojure.stacktrace/print-stack-trace and clojure.test/file-and-line throwing exceptions when given Throwable with an empty stack trace:

{noformat}
user=> (require '[clojure.stacktrace :as s])
nil
user=> (def empty-stack (into-array (Class/forName "java.lang.StackTraceElement") []))
#'user/empty-stack
user=> (def t (doto (Throwable.) (.setStackTrace empty-stack)))
#'user/t
user=> (def msg (with-out-str (s/print-stack-trace t)))

NullPointerException clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
user=> msg
#<Unbound Unbound: #'user/msg>
user=> (require 'clojure.test)
nil
user=> (def m1 (#'clojure.test/file-and-line t 0))

ArrayIndexOutOfBoundsException java.lang.reflect.Array.get (Array.java:-2)
user=> m1
#<Unbound Unbound: #'user/m1>
{noformat}

I have seen this cause confusing output when exceptions with empty stack traces are thrown while running tests on a project. According to the Java docs for Throwable, it is permissible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29


Patch: clj-1102-improve-empty-stack-trace-handling-v2.txt

Approach:

I found all places in the Clojure code that call getStackTrace. Among them, two did not handle an empty stack trace correctly.

Output of tests above with this patch applied:

{noformat}
...

user=> (def msg (with-out-str (s/print-stack-trace t)))
#'user/msg
user=> (print msg)
java.lang.Exception: null
 at [empty stack trace]
nil

...

user=> (def m1 (#'clojure.test/file-and-line t 0))
#'user/m1
user=> m1
{:line nil, :file nil}
{noformat}
Attachment clj-1102-improve-empty-stack-trace-handling-v2.txt [ 12197 ]
Andy Fingerhut made changes -
Attachment clj-1102-improve-empty-stack-trace-handling-v1.txt [ 11662 ]
Alex Miller made changes -
Approval Vetted [ 10003 ] Triaged [ 10120 ]
Alex Miller made changes -
Labels errormsgs
Rich Hickey made changes -
Approval Triaged [ 10120 ] Vetted [ 10003 ]
Fix Version/s Release 1.6 [ 10157 ]
Andy Fingerhut made changes -
Description REPL session demonstrating clojure.stacktrace/print-stack-trace and clojure.test/file-and-line throwing exceptions when given Throwable with an empty stack trace:

{noformat}
user=> (require '[clojure.stacktrace :as s])
nil
user=> (def empty-stack (into-array (Class/forName "java.lang.StackTraceElement") []))
#'user/empty-stack
user=> (def t (doto (Throwable.) (.setStackTrace empty-stack)))
#'user/t
user=> (def msg (with-out-str (s/print-stack-trace t)))

NullPointerException clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
user=> msg
#<Unbound Unbound: #'user/msg>
user=> (require 'clojure.test)
nil
user=> (def m1 (#'clojure.test/file-and-line t 0))

ArrayIndexOutOfBoundsException java.lang.reflect.Array.get (Array.java:-2)
user=> m1
#<Unbound Unbound: #'user/m1>
{noformat}

I have seen this cause confusing output when exceptions with empty stack traces are thrown while running tests on a project. According to the Java docs for Throwable, it is permissible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29


Patch: clj-1102-improve-empty-stack-trace-handling-v2.txt

Approach:

I found all places in the Clojure code that call getStackTrace. Among them, two did not handle an empty stack trace correctly.

Output of tests above with this patch applied:

{noformat}
...

user=> (def msg (with-out-str (s/print-stack-trace t)))
#'user/msg
user=> (print msg)
java.lang.Exception: null
 at [empty stack trace]
nil

...

user=> (def m1 (#'clojure.test/file-and-line t 0))
#'user/m1
user=> m1
{:line nil, :file nil}
{noformat}
REPL session demonstrating clojure.stacktrace/print-stack-trace and clojure.test/file-and-line throwing exceptions when given Throwable with an empty stack trace:

{noformat}
user=> (require '[clojure.stacktrace :as s])
nil
user=> (def empty-stack (into-array (Class/forName "java.lang.StackTraceElement") []))
#'user/empty-stack
user=> (def t (doto (Throwable.) (.setStackTrace empty-stack)))
#'user/t
user=> (def msg (with-out-str (s/print-stack-trace t)))

NullPointerException clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
user=> msg
#<Unbound Unbound: #'user/msg>
user=> (require 'clojure.test)
nil
user=> (def m1 (#'clojure.test/file-and-line t 0))

ArrayIndexOutOfBoundsException java.lang.reflect.Array.get (Array.java:-2)
user=> m1
#<Unbound Unbound: #'user/m1>
{noformat}

I have seen this cause confusing output when exceptions with empty stack traces are thrown while running tests on a project. According to the Java docs for Throwable, it is permissible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29


*Patch*: clj-1102-improve-empty-stack-trace-handling-v2.txt

*Approach*:

I found all places in the Clojure code that call getStackTrace. Among them, two did not handle an empty stack trace correctly.

Output of tests above with this patch applied:

{code}
...

user=> (def msg (with-out-str (s/print-stack-trace t)))
#'user/msg
user=> (print msg)
java.lang.Exception: null
 at [empty stack trace]
nil

...

user=> (def m1 (#'clojure.test/file-and-line t 0))
#'user/m1
user=> m1
{:line nil, :file nil}
{code}
Alex Miller made changes -
Description REPL session demonstrating clojure.stacktrace/print-stack-trace and clojure.test/file-and-line throwing exceptions when given Throwable with an empty stack trace:

{noformat}
user=> (require '[clojure.stacktrace :as s])
nil
user=> (def empty-stack (into-array (Class/forName "java.lang.StackTraceElement") []))
#'user/empty-stack
user=> (def t (doto (Throwable.) (.setStackTrace empty-stack)))
#'user/t
user=> (def msg (with-out-str (s/print-stack-trace t)))

NullPointerException clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
user=> msg
#<Unbound Unbound: #'user/msg>
user=> (require 'clojure.test)
nil
user=> (def m1 (#'clojure.test/file-and-line t 0))

ArrayIndexOutOfBoundsException java.lang.reflect.Array.get (Array.java:-2)
user=> m1
#<Unbound Unbound: #'user/m1>
{noformat}

I have seen this cause confusing output when exceptions with empty stack traces are thrown while running tests on a project. According to the Java docs for Throwable, it is permissible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29


*Patch*: clj-1102-improve-empty-stack-trace-handling-v2.txt

*Approach*:

I found all places in the Clojure code that call getStackTrace. Among them, two did not handle an empty stack trace correctly.

Output of tests above with this patch applied:

{code}
...

user=> (def msg (with-out-str (s/print-stack-trace t)))
#'user/msg
user=> (print msg)
java.lang.Exception: null
 at [empty stack trace]
nil

...

user=> (def m1 (#'clojure.test/file-and-line t 0))
#'user/m1
user=> m1
{:line nil, :file nil}
{code}
REPL session demonstrating clojure.stacktrace/print-stack-trace and clojure.test/file-and-line throwing exceptions when given Throwable with an empty stack trace:

{noformat}
user=> (require '[clojure.stacktrace :as s])
nil
user=> (def empty-stack (into-array (Class/forName "java.lang.StackTraceElement") []))
#'user/empty-stack
user=> (def t (doto (Throwable.) (.setStackTrace empty-stack)))
#'user/t
user=> (def msg (with-out-str (s/print-stack-trace t)))

NullPointerException clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
user=> msg
#<Unbound Unbound: #'user/msg>
user=> (require 'clojure.test)
nil
user=> (def m1 (#'clojure.test/file-and-line t 0))

ArrayIndexOutOfBoundsException java.lang.reflect.Array.get (Array.java:-2)
user=> m1
#<Unbound Unbound: #'user/m1>
{noformat}

I have seen this cause confusing output when exceptions with empty stack traces are thrown while running tests on a project. According to the Java docs for Throwable, it is permissible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29

*Approach*:

I found all places in the Clojure code that call getStackTrace. Among them, two did not handle an empty stack trace correctly.

Output of tests above with this patch applied:

{code}
...

user=> (def msg (with-out-str (s/print-stack-trace t)))
#'user/msg
user=> (print msg)
java.lang.Exception: null
 at [empty stack trace]
nil

...

user=> (def m1 (#'clojure.test/file-and-line t 0))
#'user/m1
user=> m1
{:line nil, :file nil}
{code}

*Patch*: clj-1102-improve-empty-stack-trace-handling-v2.txt

*Screened by:* Alex Miller
Alex Miller made changes -
Approval Vetted [ 10003 ] Screened [ 10004 ]
Alex Miller made changes -
Description REPL session demonstrating clojure.stacktrace/print-stack-trace and clojure.test/file-and-line throwing exceptions when given Throwable with an empty stack trace:

{noformat}
user=> (require '[clojure.stacktrace :as s])
nil
user=> (def empty-stack (into-array (Class/forName "java.lang.StackTraceElement") []))
#'user/empty-stack
user=> (def t (doto (Throwable.) (.setStackTrace empty-stack)))
#'user/t
user=> (def msg (with-out-str (s/print-stack-trace t)))

NullPointerException clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
user=> msg
#<Unbound Unbound: #'user/msg>
user=> (require 'clojure.test)
nil
user=> (def m1 (#'clojure.test/file-and-line t 0))

ArrayIndexOutOfBoundsException java.lang.reflect.Array.get (Array.java:-2)
user=> m1
#<Unbound Unbound: #'user/m1>
{noformat}

I have seen this cause confusing output when exceptions with empty stack traces are thrown while running tests on a project. According to the Java docs for Throwable, it is permissible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29

*Approach*:

I found all places in the Clojure code that call getStackTrace. Among them, two did not handle an empty stack trace correctly.

Output of tests above with this patch applied:

{code}
...

user=> (def msg (with-out-str (s/print-stack-trace t)))
#'user/msg
user=> (print msg)
java.lang.Exception: null
 at [empty stack trace]
nil

...

user=> (def m1 (#'clojure.test/file-and-line t 0))
#'user/m1
user=> m1
{:line nil, :file nil}
{code}

*Patch*: clj-1102-improve-empty-stack-trace-handling-v2.txt

*Screened by:* Alex Miller
REPL session demonstrating clojure.stacktrace/print-stack-trace and clojure.test/file-and-line throwing exceptions when given Throwable with an empty stack trace:

{noformat}
user=> (require '[clojure.stacktrace :as s])
nil
user=> (def empty-stack (into-array (Class/forName "java.lang.StackTraceElement") []))
#'user/empty-stack
user=> (def t (doto (Throwable.) (.setStackTrace empty-stack)))
#'user/t
user=> (def msg (with-out-str (s/print-stack-trace t)))

NullPointerException clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:296)
user=> msg
#<Unbound Unbound: #'user/msg>
user=> (require 'clojure.test)
nil
user=> (def m1 (#'clojure.test/file-and-line t 0))

ArrayIndexOutOfBoundsException java.lang.reflect.Array.get (Array.java:-2)
user=> m1
#<Unbound Unbound: #'user/m1>
{noformat}

I have seen this cause confusing output when exceptions with empty stack traces are thrown while running tests on a project. According to the Java docs for Throwable, it is permissible for getStackTrace to do this:

    http://docs.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace%28%29

*Approach*:

I found all places in the Clojure code that call getStackTrace. Among them, two did not handle an empty stack trace correctly.

Output of tests above with this patch applied:

{code}
...

user=> (def msg (with-out-str (s/print-stack-trace t)))
#'user/msg
user=> (print msg)
java.lang.Exception: null
 at [empty stack trace]
nil

...

user=> (def m1 (#'clojure.test/file-and-line t 0))
#'user/m1
user=> m1
{:line nil, :file nil}
{code}

*Patch*: clj-1102-improve-empty-stack-trace-handling-v2.diff

*Screened by:* Alex Miller
Attachment clj-1102-improve-empty-stack-trace-handling-v2.diff [ 12365 ]
Rich Hickey made changes -
Approval Screened [ 10004 ] Ok [ 10007 ]
Stuart Halloway made changes -
Resolution Completed [ 1 ]
Status Open [ 1 ] Closed [ 6 ]

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: