Skip to content

Commit 4773730

Browse files
mfikesswannodette
authored andcommitted
CLJS-1224: cljs.repl: Memoize stack frame mapping
When evaluating an expression that results in a stack overflow, and the resulting trace is source mapped by cljs.repl, this can result in thousands of frames being mapped which can take a few minutes. But, oftentimes stack overflow is the result of recursion, so many of the mapped frames are identical. This is trivial to optimize using memoize, resulting in the mapping occurring in a couple of seconds.
1 parent e3ba4f4 commit 4773730

File tree

1 file changed

+43
-40
lines changed

1 file changed

+43
-40
lines changed

src/clj/cljs/repl.clj

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,46 @@
263263
[:line :col :name])))
264264
default)))
265265

266+
(defn- mapped-frame
267+
"Given opts and a canonicalized JavaScript stacktrace frame, return the
268+
ClojureScript frame."
269+
[{:keys [function file line column]} opts]
270+
(let [no-source-file? (if-not file
271+
true
272+
(.startsWith file "<"))
273+
rfile (when-not no-source-file?
274+
(io/file (URL. (.toURL (io/file (util/output-directory opts))) file)))
275+
[sm {:keys [ns source-file] :as ns-info}]
276+
(when-not no-source-file?
277+
((juxt read-source-map ns-info) rfile))
278+
[line' column' call] (if ns-info
279+
(mapped-line-column-call sm line column)
280+
[line column])
281+
name' (when (and ns-info function)
282+
function)
283+
file' (if no-source-file?
284+
file
285+
(string/replace
286+
(.getCanonicalFile
287+
(if ns-info
288+
source-file
289+
(io/file rfile)))
290+
(str (System/getProperty "user.dir") File/separator) ""))
291+
url (or (and ns-info (util/ns->source ns))
292+
(and file (io/resource file)))]
293+
(merge
294+
{:function name'
295+
:call call
296+
:file (if no-source-file?
297+
(str "NO_SOURCE_FILE"
298+
(when file
299+
(str " " file)))
300+
(io/file file'))
301+
:line line'
302+
:column column'}
303+
(when url
304+
{:url url}))))
305+
266306
(defn mapped-stacktrace
267307
"Given a vector representing the canonicalized JavaScript stacktrace
268308
return the ClojureScript stacktrace. The canonical stacktrace must be
@@ -280,55 +320,18 @@
280320
([stacktrace] (mapped-stacktrace stacktrace nil))
281321
([stacktrace opts]
282322
(vec
283-
(let [with-calls
284-
(for [{:keys [function file line column] :as frame} stacktrace]
285-
;; need to convert file, a relative URL style path, to host-specific file
286-
(let [no-source-file? (if-not file
287-
true
288-
(.startsWith file "<"))
289-
rfile (when-not no-source-file?
290-
(io/file (URL. (.toURL (io/file (util/output-directory opts))) file)))
291-
[sm {:keys [ns source-file] :as ns-info}]
292-
(when-not no-source-file?
293-
((juxt read-source-map ns-info) rfile))
294-
[line' column' call] (if ns-info
295-
(mapped-line-column-call sm line column)
296-
[line column])
297-
name' (when (and ns-info function)
298-
function)
299-
file' (if no-source-file?
300-
file
301-
(string/replace
302-
(.getCanonicalFile
303-
(if ns-info
304-
source-file
305-
(io/file rfile)))
306-
(str (System/getProperty "user.dir") File/separator) ""))
307-
url (or (and ns-info (util/ns->source ns))
308-
(and file (io/resource file)))]
309-
(merge
310-
{:function name'
311-
:call call
312-
:file (if no-source-file?
313-
(str "NO_SOURCE_FILE"
314-
(when file
315-
(str " " file)))
316-
(io/file file'))
317-
:line line'
318-
:column column'}
319-
(when url
320-
{:url url}))))]
323+
(let [mapped-frames (map (memoize #(mapped-frame % opts)) stacktrace)]
321324
;; take each non-nil :call and optionally merge it into :function one-level up
322325
;; to avoid replacing with local symbols, we only replace munged name if we can munge call symbol back to it
323326
(map #(merge-with (fn [munged-fn-name unmunged-call-name]
324327
(if (= munged-fn-name (string/replace (cljs.compiler/munge unmunged-call-name) "." "$"))
325328
unmunged-call-name
326329
munged-fn-name)) %1 %2)
327-
(map #(dissoc % :call) with-calls)
330+
(map #(dissoc % :call) mapped-frames)
328331
(concat (rest (map #(if (:call %)
329332
(hash-map :function (:call %))
330333
{})
331-
with-calls)) [{}]))))))
334+
mapped-frames)) [{}]))))))
332335

333336
(defn print-mapped-stacktrace
334337
"Given a vector representing the canonicalized JavaScript stacktrace

0 commit comments

Comments
 (0)