mfikes / ambly

ClojureScript REPL into embedded JavaScriptCore
http://ambly.fikesfarm.com
Eclipse Public License 1.0
541 stars 21 forks source link

source map support #10

Closed swannodette closed 9 years ago

swannodette commented 9 years ago

ClojureScript has source map support at runtime on the client or this can be done on the server. Server side support needs a tiny push. This is probably the right way to do it. Then any JavaScript environment that's properly bootstrapped can send the stacktrace and the ClojureScript REPL can map it for you. This should probably be the default and be disable-able for environment likes Node.js that already have good source mapping support.

mfikes commented 9 years ago

@swannodette By "server side support", did you mean something upstream in the ClojureScript codebase? (Or, something in the Objective-C code in this repo?)

swannodette commented 9 years ago

@mfikes I just landed some helpers in the ClojureScript cljs.source-map namespace to make it easier to remap stack traces from REPLs.

mfikes commented 9 years ago

From irc

dnolen mfikes: should maybe think about how we parse traces since ever engine is a little bit different dnolen mfikes: perhaps this should be the job of the REPL before passing it on to the generic REPL infrastructure

mfikes commented 9 years ago

The V8 stacktrace format is documented here.

I find a couple of things interesting:

  1. They have an object model you can use to directly access frames and their properties.
  2. The example with nesting eval near the bottom could defeat a naive parser.
mfikes commented 9 years ago

@swannodette Let me know if you'd like me to take a stab at rewriting the "symbol@file-url:line:column" JSC-style stack traces to look like V8 in the Objective-C code near here.

(That is, if you think that's the right direction and if you think that would help.)

swannodette commented 9 years ago

@mfikes I think stack canonicalization should always happen on the Clojure side. And matching V8 isn't a goal, we want a Clojure representation that can easily be source mapped. Having this stuff in Clojure means it's more visible and that potential REPL writers can more easily understand the minimal set of functionality necessary for a good experience.

mfikes commented 9 years ago

@swannodette Yes. I came to the same conclusion and came back to suggest that alternative. :)

Perhaps some conditional code here if :exception?

mfikes commented 9 years ago

@swannodette Also agree on non-V8 rep.

mfikes commented 9 years ago

Hmm... stack canonicalization might be a challenge for REPLs that support arbitrary JavaScript engines. (For example, the browser REPL, or Weasel).

swannodette commented 9 years ago

@mfikes I'm not too concerned about that. Multi-target REPLs will need to sort out the best way to do stack canonicalization themselves. Automatic source mapping of stack traces will be behind a REPL flag with the default set to false.

mfikes commented 9 years ago

@swannodette Landed a stab at the parsing aspect (feel free to revise).

For

(first (js/Date.))

It parses it apart into:

{:frames 
  '({:function "seq" 
     :file "/Users/mfikes/Documents/Projects/ambly/Clojure/out/goog/../cljs/core.js"
     :line 4212
     :column 17} 
   {:function "first"
     :file "/Users/mfikes/Documents/Projects/ambly/Clojure/out/goog/../cljs/core.js"
     :line 4242
     :column 22})
   :stack "Error: Fri Feb 06 2015 14:19:32 GMT-0500 (EST) is not ISeqable\nseq@file:///Users/mfikes/Documents/Projects/ambly/Clojure/out/goog/../cljs/core.js:4212:17\nfirst@file:///Users/mfikes/Documents/Projects/ambly/Clojure/out/goog/../cljs/core.js:4242:22\n\nglobal code"}
mfikes commented 9 years ago

With the last set of changes, here is what you see in the REPL (note that there is now a convenience script/jscrepljs to start up the Clojure(Script) half):

Mike-Fikess-MacBook-Pro:Clojure mfikes$ script/jscrepljs 
To quit, type: :cljs/quit
ClojureScript:cljs.user> (first (js/Date.))
Error: Fri Feb 06 2015 20:42:42 GMT-0500 (EST) is not ISeqable
[{:function seq, :file /Users/mfikes/Documents/Projects/ambly/Clojure/.ambly_jsc_repl/goog/../cljs/core.js, :line 4212, :column 17} {:function first, :file /Users/mfikes/Documents/Projects/ambly/Clojure/.ambly_jsc_repl/goog/../cljs/core.js, :line 4242, :column 22}]
nil

Of note:

[{:function "seq", :file "/Users/mfikes/Documents/Projects/ambly/Clojure/.ambly_jsc_repl/goog/../cljs/core.js" :line 4212 :column 17} {:function "first" :file "/Users/mfikes/Documents/Projects/ambly/Clojure/.ambly_jsc_repl/goog/../cljs/core.js" :line 4242 :column 22}]

mfikes commented 9 years ago

Here is a little more detail with what I was seeing with ClojureScript master:

The Node.js REPL is producing line numbers that make sense when you go and open the files:

ClojureScript:cljs.user> (ffirst 1)
Error: 1 is not ISeqable
    at Object.seq (/Users/mfikes/Documents/Projects/clojurescript2/.cljs_node_repl/cljs/core.cljs:727:20)
    at Object.first (/Users/mfikes/Documents/Projects/clojurescript2/.cljs_node_repl/cljs/core.cljs:736:16)
    at ffirst (/Users/mfikes/Documents/Projects/clojurescript2/.cljs_node_repl/cljs/core.cljs:1155:11)

Ambly's are off a bit (not off-by-one):

ClojureScript:cljs.user> (ffirst 1)
Error: 1 is not ISeqable
cljs.core/seq (.ambly_jsc_repl/cljs/core.cljs:711:4)
cljs.core/first (.ambly_jsc_repl/cljs/core.cljs:735:6)
cljs.core/ffirst (.ambly_jsc_repl/cljs/core.cljs:1151:0)
swannodette commented 9 years ago

This issue has been resolved in this repo and ClojureScript master now supports.