Skip to content

Commit e3df5da

Browse files
sgroveswannodette
authored andcommitted
Fixes the off-by-one line numbering in Chrome source inspector, and emits column information for several binding forms.
1 parent e917034 commit e3df5da

File tree

3 files changed

+119
-27
lines changed

3 files changed

+119
-27
lines changed

samples/hello/src/hello/core.cljs

+14
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,17 @@
66

77
(defn ^:export sum [xs]
88
(bar/sum xs))
9+
10+
(defn ^:export let-works? [day month year]
11+
(let [hour (first day)
12+
minutes (first hour)
13+
seconds (last hour)]
14+
(.log js/console "Date: " year month day)
15+
(str year month day hour minutes seconds)))
16+
17+
(defn bailey [proton neutron electron & comedies]
18+
(apply + proton neutron electron)
19+
(map identity comedies))
20+
21+
(defn videotape [& rainbows]
22+
(map :params rainbows))

src/clj/cljs/analyzer.clj

+20-2
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,17 @@
395395
:line (get-line name env)
396396
:column (get-col name env)
397397
:tag (-> name meta :tag)
398-
:shadow (when locals (locals name))}]
398+
:shadow (when locals (locals name))
399+
;; Give the fn params the same shape
400+
;; as a :var, so it gets routed
401+
;; correctly in the compiler
402+
:op :var
403+
:env (merge (select-keys env [:context])
404+
{:line (get-line name env)
405+
:column (get-col name env)})
406+
:info {:name name
407+
:shadow (when locals (locals name))}
408+
:binding-form? true}]
399409
[(assoc locals name param) (conj params param)]))
400410
[locals []] param-names)
401411
fixed-arity (count (if variadic (butlast params) params))
@@ -520,7 +530,15 @@
520530
(-> init-expr :tag)
521531
(-> init-expr :info :tag))
522532
:local true
523-
:shadow (-> env :locals name)}
533+
:shadow (-> env :locals name)
534+
;; Give let* bindings same shape as var so
535+
;; they get routed correctly in the compiler
536+
:op :var
537+
:env {:line (get-line name env)
538+
:column (get-col name env)}
539+
:info {:name name
540+
:shadow (-> env :locals name)}
541+
:binding-form? true}
524542
be (if (= (:op init-expr) :fn)
525543
(merge be
526544
{:fn-var true

src/clj/cljs/compiler.clj

+85-25
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,22 @@
4646

4747
(defonce ns-first-segments (atom '#{"cljs" "clojure"}))
4848

49+
; Helper fn
50+
(defn shadow-depth [s]
51+
(let [{:keys [name info]} s]
52+
(loop [d 0, {:keys [shadow]} info]
53+
(cond
54+
shadow (recur (inc d) shadow)
55+
(@ns-first-segments (str name)) (inc d)
56+
:else d))))
57+
4958
(defn munge
5059
([s] (munge s js-reserved))
5160
([s reserved]
5261
(if (map? s)
5362
; Unshadowing
5463
(let [{:keys [name field] :as info} s
55-
depth (loop [d 0, {:keys [shadow]} info]
56-
(cond
57-
shadow (recur (inc d) shadow)
58-
(@ns-first-segments (str name)) (inc d)
59-
:else d))
64+
depth (shadow-depth s)
6065
renamed (*lexical-renames* (System/identityHashCode s))
6166
munged-name (munge (cond field (str "self__." name)
6267
renamed renamed
@@ -220,13 +225,23 @@
220225
(let [minfo {:gcol @*cljs-gen-col*
221226
:gline @*cljs-gen-line*
222227
:name var-name}]
223-
(update-in m [line]
228+
; Dec the line number for 0-indexed line numbers.
229+
; tools.reader has 0-indexed line number, chrome
230+
; expects 1-indexed source maps.
231+
(update-in m [(dec line)]
224232
(fnil (fn [m]
225233
(update-in m [(or column 0)]
226234
(fnil (fn [v] (conj v minfo)) [])))
227235
(sorted-map)))))))))
228-
(when-not (= :statement (:context env))
229-
(emit-wrap env (emits (munge info))))))
236+
; We need a way to write bindings out to source maps and javascript
237+
; without getting wrapped in an emit-wrap calls, otherwise we get
238+
; e.g. (function greet(return x, return y) {}).
239+
(if (:binding-form? arg)
240+
; Emit the arg map so shadowing is properly handled when munging
241+
; (prevents duplicate fn-param-names)
242+
(emits (munge arg))
243+
(when-not (= :statement (:context env))
244+
(emit-wrap env (emits (munge info)))))))
230245

231246
(defmethod emit :meta
232247
[{:keys [expr meta env]}]
@@ -364,27 +379,57 @@
364379
(defn emit-apply-to
365380
[{:keys [name params env]}]
366381
(let [arglist (gensym "arglist__")
367-
delegate-name (str (munge name) "__delegate")
368-
params (map munge params)]
382+
delegate-name (str (munge name) "__delegate")]
369383
(emitln "(function (" arglist "){")
370384
(doseq [[i param] (map-indexed vector (drop-last 2 params))]
371-
(emits "var " param " = cljs.core.first(")
385+
(emits "var ")
386+
(emit param)
387+
(emits " = cljs.core.first(")
372388
(emitln arglist ");")
373389
(emitln arglist " = cljs.core.next(" arglist ");"))
374390
(if (< 1 (count params))
375391
(do
376-
(emitln "var " (last (butlast params)) " = cljs.core.first(" arglist ");")
377-
(emitln "var " (last params) " = cljs.core.rest(" arglist ");")
378-
(emitln "return " delegate-name "(" (string/join ", " params) ");"))
392+
(emits "var ")
393+
(emit (last (butlast params)))
394+
(emitln " = cljs.core.first(" arglist ");")
395+
(emits "var ")
396+
(emit (last params))
397+
(emitln " = cljs.core.rest(" arglist ");")
398+
(emits "return " delegate-name "(")
399+
(doseq [param params]
400+
(emit param)
401+
(when-not (= param (last params)) (emits ",")))
402+
(emitln ");"))
379403
(do
380-
(emitln "var " (last params) " = cljs.core.seq(" arglist ");")
381-
(emitln "return " delegate-name "(" (string/join ", " params) ");")))
404+
(emits "var ")
405+
(emit (last params))
406+
(emitln " = cljs.core.seq(" arglist ");")
407+
(emits "return " delegate-name "(")
408+
(doseq [param params]
409+
(emit param)
410+
(when-not (= param (last params)) (emits ",")))
411+
(emitln ");")))
382412
(emits "})")))
383413

414+
(defn emit-fn-params [params]
415+
(doseq [param params]
416+
(emit param)
417+
; Avoid extraneous comma (function greet(x, y, z,)
418+
(when-not (= param (last params))
419+
(emits ","))))
420+
384421
(defn emit-fn-method
385422
[{:keys [type name variadic params expr env recurs max-fixed-arity]}]
386423
(emit-wrap env
387-
(emitln "(function " (munge name) "(" (comma-sep (map munge params)) "){")
424+
; Should we emit source-map for this inner declaration?
425+
; It may be unnecessary.
426+
; hello.core.greet = (function greet(){})
427+
; e.g. Do we need a source-map entry for this? --^
428+
429+
; If so, we can't just munge the name and spit out a string.
430+
(emits "(function " (munge name) "(")
431+
(emit-fn-params params)
432+
(emits "){")
388433
(when type
389434
(emitln "var self__ = this;"))
390435
(when recurs (emitln "while(true){"))
@@ -399,10 +444,13 @@
399444
(emit-wrap env
400445
(let [name (or name (gensym))
401446
mname (munge name)
402-
params (map munge params)
403447
delegate-name (str mname "__delegate")]
404448
(emitln "(function() { ")
405-
(emitln "var " delegate-name " = function (" (comma-sep params) "){")
449+
(emits "var " delegate-name " = function (")
450+
(doseq [param params]
451+
(emit param)
452+
(when-not (= param (last params)) (emits ",")))
453+
(emits "){")
406454
(when recurs (emitln "while(true){"))
407455
(emits expr)
408456
(when recurs
@@ -417,11 +465,19 @@
417465
(when type
418466
(emitln "var self__ = this;"))
419467
(when variadic
420-
(emitln "var " (last params) " = null;")
468+
(emits "var ")
469+
(emit (last params))
470+
(emits " = null;")
421471
(emitln "if (arguments.length > " (dec (count params)) ") {")
422-
(emitln " " (last params) " = cljs.core.array_seq(Array.prototype.slice.call(arguments, " (dec (count params)) "),0);")
472+
(emits " ")
473+
(emit (last params))
474+
(emits " = cljs.core.array_seq(Array.prototype.slice.call(arguments, " (dec (count params)) "),0);")
423475
(emitln "} "))
424-
(emitln "return " delegate-name ".call(" (string/join ", " (cons "this" params)) ");")
476+
(emits "return " delegate-name ".call(this,")
477+
(doseq [param params]
478+
(emit param)
479+
(when-not (= param (last params)) (emits ",")))
480+
(emits ");")
425481
(emitln "};")
426482

427483
(emitln mname ".cljs$lang$maxFixedArity = " max-fixed-arity ";")
@@ -453,7 +509,7 @@
453509
(let [has-name? (and name true)
454510
name (or name (gensym))
455511
mname (munge name)
456-
maxparams (map munge (apply max-key count (map :params methods)))
512+
maxparams (apply max-key count (map :params methods))
457513
mmap (into {}
458514
(map (fn [method]
459515
[(munge (symbol (str mname "__" (count (:params method)))))
@@ -474,7 +530,9 @@
474530
(concat (butlast maxparams) ['var_args])
475531
maxparams)) "){")
476532
(when variadic
477-
(emitln "var " (last maxparams) " = var_args;"))
533+
(emits "var ")
534+
(emit (last maxparams))
535+
(emitln " = var_args;"))
478536
(emitln "switch(arguments.length){")
479537
(doseq [[n meth] ms]
480538
(if (:variadic meth)
@@ -540,7 +598,9 @@
540598
(gensym (str (:name %) "-")))
541599
bindings)))]
542600
(doseq [{:keys [init] :as binding} bindings]
543-
(emitln "var " (munge binding) " = " init ";"))
601+
(emits "var ")
602+
(emit binding) ; Binding will be treated as a var
603+
(emits " = " init ";"))
544604
(when is-loop (emitln "while(true){"))
545605
(emits expr)
546606
(when is-loop

0 commit comments

Comments
 (0)