ex-info loses stack information

Description

Native js Error keeps stacktrace:

(js/console.log (.-stack (js/Error. "message"))) Error: message at eval (/Users/prokopov/Dropbox/ws/datascript/test/test/datascript.cljs[eval5]:10:14) at eval (native) at SocketNamespace.<anonymous> (http://localhost:65000/socket.io/lighttable/ws.js:118:26) at SocketNamespace.EventEmitter.emit [as $emit] (http://localhost:65000/socket.io/socket.io.js:633:15) at SocketNamespace.onPacket (http://localhost:65000/socket.io/socket.io.js:2248:20) at Socket.onPacket (http://localhost:65000/socket.io/socket.io.js:1930:30) at Transport.onPacket (http://localhost:65000/socket.io/socket.io.js:1332:17) at Transport.onData (http://localhost:65000/socket.io/socket.io.js:1303:16) at WebSocket.websocket.onmessage (http://localhost:65000/socket.io/socket.io.js:2378:12)

But ex-info does not:

(js/console.log (.-stack (ex-info "message"))) Error at file:///Users/prokopov/Dropbox/ws/datascript/web/target-cljs/cljs/core.js:32066:38

Problem is that ex-info inherits stack property from prototype which is instantiated at script load time here:

(deftype ExceptionInfo [message data cause]) (set! (.-prototype ExceptionInfo) (js/Error.)) (set! (.. ExceptionInfo -prototype -constructor) ExceptionInfo)

The possible solution is to create new instance of js/Error at (ex-info) and manually copy stack property to ExceptionInfo object. Related SO: http://stackoverflow.com/questions/783818/how-do-i-create-a-custom-error-in-javascript

Problem is that Chrome has setter on stack property, and it only allows for this property to be set inside a constructor functions.

Proposed fix creates new Error each time ex-info is called and sets ExceptionInfo.prototype to newly created error. This way new ExceptionInfo instance will inherit stack from newly created Error with correct stack.

This patch has been tested in Chrome 39 Mac, Safari 8 Mac, Firefox 35 Mac and IE 10 Win. Here's test code I used:

(defn -ex-info ([msg data] (set! (.-prototype ExceptionInfo) (js/Error msg)) (set! (.. ExceptionInfo -prototype -name) "ExceptionInfo") (set! (.. ExceptionInfo -prototype -constructor) ExceptionInfo) (ExceptionInfo. msg data nil)) ([msg data cause] (set! (.-prototype ExceptionInfo) (js/Error msg)) (set! (.. ExceptionInfo -prototype -name) "ExceptionInfo") (set! (.. ExceptionInfo -prototype -constructor) ExceptionInfo) (ExceptionInfo. msg data cause))) (try (throw (ex-info "[ -- Current ex-info message -- ]" 123)) (catch ExceptionInfo e (js/console.log "Current ex-info::" (.-stack e)))) (try (throw (js/Error "[ -- Native message -- ]")) (catch js/Error e (js/console.log "Native error::" (.-stack e)))) (try (throw (-ex-info "[ -- Patched ex-info message -- ]" 123)) (catch ExceptionInfo e (js/console.log "Patched ex-info::" (.-stack e))))

Test results:

Chrome, Firefox, IE, Safari

Note that current implementation reports line number and overall stacktrace from cljs.core file where Error prototype is created in current implementation.
Note that patched version reports correct line number (it should be close to native error stack), stack, message and exception name.
Also note that IE is fine even without patch — that's because in IE stack is capturead at throw place, not at new Error() call site.

Environment

None

Attachments

1
  • 22 Jan 2015, 09:35 AM

Activity

Show:

David NolenJuly 2, 2015 at 11:59 PM

fixed in master

importJuly 2, 2015 at 1:19 AM

Comment made by: matklad

Looks like it should be reopened because of this commit
https://github.com/clojure/clojurescript/commit/de130a335bd761d580d04dadb8a5a43d3c0a35b4

js/Error is constructed with empty message on this line https://github.com/clojure/clojurescript/commit/de130a335bd761d580d04dadb8a5a43d3c0a35b4#diff-a98a037c6c098dd3707e861df3c2f5acR9197

So, when stack is assigned here
https://github.com/clojure/clojurescript/commit/de130a335bd761d580d04dadb8a5a43d3c0a35b4#diff-a98a037c6c098dd3707e861df3c2f5acR9210
it does not have a message.

I guess that the fix will be to change `(let [e (js/Error.)]` to `(let [e (js/Error. message)]`

David NolenJanuary 22, 2015 at 8:24 PM

Nikita, thanks for the update will check it out.

Nikita ProkopovJanuary 22, 2015 at 9:50 AM

David, I updated issue, added patch, test code and test results (including IE). There’s no unit test on this because stack traces are very engine-specific. Please take a look

Completed

Details

Assignee

Reporter

Priority

Created January 20, 2015 at 8:28 PM
Updated July 2, 2015 at 11:59 PM
Resolved July 2, 2015 at 11:59 PM