ClojureScript Stacktraces for JavaScriptCore

For quite a while, ClojureScript has had support for source level stacktraces, meaning that the file, line, and column information in stacktraces refers to the original ClojureScript source, as opposed to the generated JavaScript in which the exceptions are thrown.

Here is an example (produced with the Node.js REPL):

ClojureScript:cljs.user> (ffirst (js/Date.)) Error: Mon Feb 09 2015 21:33:02 GMT-0500 (EST) is not ISeqable at Object.seq (.../cljs/core.cljs:727:20) at Object.first (.../cljs/core.cljs:736:16) at ffirst (.../cljs/core.cljs:1155:11) ...

I've been developing hybrid ClojureScript / iOS apps for some time , where the JavaScript runs inside JavaScriptCore, but the “source mapping” needed to pull this off wasn't there, and the resulting stacktraces were not so friendly.

Until now… Recently, I've been involved in some work with David Nolen, the goal being to prepare for support for Om targeting React Native on iOS. We've been fleshing out some infrastructure, and in the process, (largely due to some work on David's part in the ClojureScript compiler), we were able to produce source level stacktraces for JavaScriptCore as well.

Here is the same example, using the new Ambly JavaScriptCore REPL we've been working on for the above:

ClojureScript:cljs.user> (ffirst (js/Date.)) Error: Mon Feb 09 2015 21:41:14 GMT-0500 (EST) is not ISeqable cljs.core/seq (.../cljs/core.cljs:727:13) cljs.core/first (.../cljs/core.cljs:736:7) cljs.core/ffirst (.../cljs/core.cljs:1155:3) ...

Very nice! The cool thing is that the support that David landed in the ClojureScript 0.0-2814 release is generic and can be used by any REPL against any JavaScript engine. All that is required is that the REPL parse the native stacktrace format emitted by the engine (in JavaScriptCore's case we are currently using a simple regex), and turn that into a simple Clojure data structure containing the relevant info.

Also in that release is a small related bonus: ClojureScript REPLs now support *e , the last exception caught (a feature already available in Clojure REPLs):

ClojureScript:cljs.user> *e #<Error: Mon Feb 09 2015 21:33:02 GMT-0500 (EST) is not ISeqable>

Fun stuff!