<< Back to previous view

[CLJS-1697] doc on inferred macros fails Created: 30/Jun/16  Updated: 30/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

If you make use of the new CLJS-1507 feature to infer a macro var in :refer, then doc fails on it.

Here is an example. See how it fails in foo.core but succeeds in bar.core:

$ script/noderepljs
ClojureScript Node.js REPL server listening on 52639
To quit, type: :cljs/quit
cljs.user=> (doc doc)
-------------------------
cljs.repl/doc
([name])
Macro
  Prints documentation for a var or special form given its name

nil
cljs.user=> (ns foo.core)
nil
foo.core=> (require '[cljs.repl :refer [doc]])
nil
foo.core=> (doc doc)
-------------------------
cljs.repl/doc
  nil

nil
foo.core=> (ns bar.core)
nil
bar.core=> (require-macros '[cljs.repl :refer [doc]])
nil
bar.core=> (doc doc)
-------------------------
cljs.repl/doc
([name])
Macro
  Prints documentation for a var or special form given its name

nil





[CLJS-1696] Alias clojure.spec.gen => cljs.spec.impl.gen Created: 29/Jun/16  Updated: 29/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Shaun LeBron Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1696.patch    

 Description   

A special case for http://dev.clojure.org/jira/browse/CLJS-1692 to correctly alias clojure.spec.gen.



 Comments   
Comment by Shaun LeBron [ 29/Jun/16 12:02 PM ]

code and test

Comment by David Nolen [ 29/Jun/16 2:04 PM ]

I'm not excited about special casing this one. I'd rather wait to see if people actually use this namespace frequently enough for this to be desirable.

Comment by Shaun LeBron [ 29/Jun/16 10:37 PM ]

yeah, i guess the question is who should be responsible for handling this special case-- the user with reader conditionals or the compiler with this patch.

for extra context, I asked about this last week in slack: https://clojurians.slack.com/archives/clojurescript/p1466866626001218





[CLJS-1695] Self-host: Port cljs / clojure namespace aliasing Created: 25/Jun/16  Updated: 25/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Unresolved Votes: 0
Labels: bootstrap


 Description   

CLJS-1692 covers auto-aliasing clojure.* to cljs.* in JVM ClojureScript. This asks for the same for bootstrapped ClojureScript.



 Comments   
Comment by Mike Fikes [ 25/Jun/16 11:47 PM ]

This issue has the use of util/ns->source in common with CLJS-1657. (Perhaps a common solution could be found for both—like making use of something along the lines of the bootstrap source load function.)





[CLJS-1685] Incorrectly lazy subvec when start param is nil Created: 17/Jun/16  Updated: 17/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.36
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Alf Kristian Støyle Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

ClojureScript 1.9.36 on Mac and Windows



 Description   

subvec in ClojureScript does not fail when start param is nil. This is different than in regular Clojure.

In Clojure:

(def foo (subvec nil 1))
CompilerException java.lang.IndexOutOfBoundsException, compiling:(form-init4645269128697935824.clj:1:10) 
(def foo (subvec nil nil))
CompilerException java.lang.NullPointerException, compiling:(form-init4645269128697935824.clj:1:10)

In ClojureScript:

(def foo (subvec nil 1))
#object[Error Error: Index out of bounds]
   cljs.core/build-subvec (jar:file:/Users/stoyle/.m2/repository/org/clojure/clojurescript/1.9.36/clojurescript-1.9.36.jar!/cljs/core.cljs:5316:16)
   Function.cljs.core.subvec.cljs$core$IFn$_invoke$arity$3 (jar:file:/Users/stoyle/.m2/repository/org/clojure/clojurescript/1.9.36/clojurescript-1.9.36.jar!/cljs/core.cljs:5328:7)
   Function.cljs.core.subvec.cljs$core$IFn$_invoke$arity$2 (jar:file:/Users/stoyle/.m2/repository/org/clojure/clojurescript/1.9.36/clojurescript-1.9.36.jar!/cljs/core.cljs:5326:7)
   cljs$core$subvec (jar:file:/Users/stoyle/.m2/repository/org/clojure/clojurescript/1.9.36/clojurescript-1.9.36.jar!/cljs/core.cljs:5319:1)
=> nil
(def foo (subvec nil nil))
=> #'user/foo

foo is of course not usable after this:

foo
#object[Error Error: No protocol method IIndexed.-nth defined for type null: ]
   cljs.core/missing-protocol (jar:file:/Users/stoyle/.m2/repository/org/clojure/clojurescript/1.9.36/clojurescript-1.9.36.jar!/cljs/core.cljs:264:4)





[CLJS-1682] :foreign-libs with module conversion does not works properly if it is used form deps.cljs Created: 13/Jun/16  Updated: 14/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.36, 1.9.76
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Andrey Antukh Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: closure, compiler, foreign-libs
Environment:

Linux, openjdk8


Attachments: Text File CLJS-1682.patch    
Patch: Code

 Description   

When :foreign-libs is used for consume commonjs (or amd) modules from clojurescript using the `deps.cljs` mechanism, an unexpected "internal compiler error" is raised. When the same configuration is attached on the build script, everything works as expected.

Simple way to reproduce that, create sample directory tree:

mkdir tmp;
cd tmp;
mkdir -p src/testapp
mkdir -p src/vendor
touch src/testapp/core.cljs
touch src/vendor/greeter.js

Download the latest compiler or copy the recently build one from master:

wget https://github.com/clojure/clojurescript/releases/download/r1.9.36/cljs.jar

Create the sample cljs file:

;; tmp/src/testapp/core.cljs
(ns testapp.core
  (:require [cljs.nodejs :as nodejs]
            [greeter.core :as g]))

(nodejs/enable-util-print!)

(defn -main
  [& args]
  (println (g/sayHello "Ciri")))

(set! *main-cli-fn* -main)

Create the sample commonjs module:

"use strict";

exports.sayHello = function(name) {
  return `Hello ${name}!`;
};

Create the build script (that works):

;; tmp/build.clj
(require '[cljs.build.api :as b])

(b/build "src"
 {:main 'testapp.core
  :output-to "out/main.js"
  :output-dir "out"
  :target :nodejs
  :language-in  :ecmascript6
  :language-out :ecmascript5
  :foreign-libs [{:file "vendor/greeter.js"
                  :module-type :commonjs
                  :provides ["greeter.core"]}]
  :verbose true})

And compile this using the following command:

java -cp cljs.jar:src clojure.main build.clj

This will generate successfully the final artifact that can be successufully executed with node:

node out/main.js
# => "Hello Ciri!"

But, if you remove the `:foreign-libs` from the build script and create a new `src/deps.cljs` file
with the following content:

{:foreign-libs [{:file "vendor/greeter.js"
                 :module-type :commonjs
                 :provides ["greeter.core"]}]}

And try compile it:

$ java -cp cljs.jar:src clojure.main build.clj
Copying jar:file:/home/niwi/tmp/cljs.jar!/cljs/core.cljs to out/cljs/core.cljs
Reading analysis cache for jar:file:/home/niwi/tmp/cljs.jar!/cljs/core.cljs
Compiling out/cljs/core.cljs
Using cached cljs.core out/cljs/core.cljs
Copying jar:file:/home/niwi/tmp/cljs.jar!/cljs/nodejs.cljs to out/cljs/nodejs.cljs
Compiling out/cljs/nodejs.cljs
Compiling src/testapp/core.cljs
Copying jar:file:/home/niwi/tmp/cljs.jar!/cljs/nodejs.cljs to out/cljs/nodejs.cljs
Copying jar:file:/home/niwi/tmp/cljs.jar!/cljs/nodejscli.cljs to out/cljs/nodejscli.cljs
Copying jar:file:/home/niwi/tmp/cljs.jar!/goog/base.js to out/goog/base.js
Copying jar:file:/home/niwi/tmp/cljs.jar!/goog/string/string.js to out/goog/string/string.js
Copying jar:file:/home/niwi/tmp/cljs.jar!/goog/object/object.js to out/goog/object/object.js
Copying jar:file:/home/niwi/tmp/cljs.jar!/goog/string/stringbuffer.js to out/goog/string/stringbuffer.js
Copying jar:file:/home/niwi/tmp/cljs.jar!/goog/debug/error.js to out/goog/debug/error.js
Copying jar:file:/home/niwi/tmp/cljs.jar!/goog/dom/nodetype.js to out/goog/dom/nodetype.js
Copying jar:file:/home/niwi/tmp/cljs.jar!/goog/asserts/asserts.js to out/goog/asserts/asserts.js
Copying jar:file:/home/niwi/tmp/cljs.jar!/goog/array/array.js to out/goog/array/array.js
Copying file:/home/niwi/tmp/src/vendor/greeter.js to out/greeter.js
Exception in thread "main" java.lang.RuntimeException: INTERNAL COMPILER ERROR.
Please report this problem.

null
  Node(SCRIPT): vendor/greeter.js:1:0
[source unknown]
  Parent: NULL, compiling:(/home/niwi/tmp/build.clj:3:1)
        at clojure.lang.Compiler.load(Compiler.java:7391)
        at clojure.lang.Compiler.loadFile(Compiler.java:7317)
        at clojure.main$load_script.invokeStatic(main.clj:275)
        at clojure.main$script_opt.invokeStatic(main.clj:335)
        at clojure.main$script_opt.invoke(main.clj:330)
        at clojure.main$main.invokeStatic(main.clj:421)
        at clojure.main$main.doInvoke(main.clj:384)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.lang.Var.invoke(Var.java:379)
        at clojure.lang.AFn.applyToHelper(AFn.java:154)
        at clojure.lang.Var.applyTo(Var.java:700)
        at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: INTERNAL COMPILER ERROR.
Please report this problem.

[...]


 Comments   
Comment by Andrey Antukh [ 13/Jun/16 5:42 AM ]

Also happens with `cljs.jar` build from master.

Comment by Rohit Aggarwal [ 14/Jun/16 5:40 AM ]

[Compiler noob here]

Here is what is causing the issue:

In src/main/clojure/cljs/closure.clj in process-js-modules function, in the first case :foreign-libs is being set in opts and in the second failing case :ups-foreign-libs is being set in opts.

I am investigating the root of this.

Comment by Rohit Aggarwal [ 14/Jun/16 6:11 AM ]

A fix is to that set foreign-libs keyword in opts to a union of both foreign-libs and ups-foreign-libs.

I've verified that it works for both the above given examples. But I don't know enough about the compiler to propose this change.

Comment by Rohit Aggarwal [ 14/Jun/16 10:57 AM ]

Attaching patch with fixes this problem. The patch keeps the two sets of data (ups-foreign-libs, foreign-libs) separate.

I've run all the tests and they pass.





[CLJS-1681] Make instrument-all / unstrument-all return nil Created: 11/Jun/16  Updated: 11/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1681.patch    
Patch: Code and Test

 Description   

In Clojure, instrument-all and unstrument-all return nil, but in ClojureScript will return the last var instrumented / unstrumented by the resulting macroexpansion.



 Comments   
Comment by Mike Fikes [ 11/Jun/16 6:01 PM ]

The attached CLJS-1681 broadens the scope a little and also takes care of instrument-ns / unstrument-ns.





[CLJS-1678] variadic defn can be called for missing fixed arities, overlapping arity Created: 11/Jun/16  Updated: 12/Jun/16

Status: Reopened
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Francis Avila Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

For defns with a variadic arity: if invoked with a missing fixed arity, they use the variadic method instead of erroring; if invoked with a fixed arity that is the max fixed arity, variadic mathod instead of fixed form is invoked.

(defn f-hole
  ([a] 1)
  ([a b c d & args] "4 or more"))

(f-hole 1 2) ; =>"4 or more", should be error

(defn f-overlap-mfa
  ([a b] 2)
  ([a b & c] "2+"))

(f-overlap-mfa 1) ;=> "2+", should be error
(f-overlap-mfa 1 2) ;=> "2+", should be 2
(f-overlap-mfa 1 2 3) ;=> "2+", correct

A way to fix the f-hole bug is to emit a "case X:" into the switch statement for all X with no signature or less than max-fixed-arity.

The f-overlap-mfa I'm not sure why is happening and didn't investigate deeply.



 Comments   
Comment by Francis Avila [ 11/Jun/16 8:31 AM ]

Sorry, filed against CLJ instead of CLJS!

Comment by Rohit Aggarwal [ 12/Jun/16 9:41 AM ]

The behaviour I am seeing for f-overlap-mfa is:

(f-overlap-mfa 1) ;=> "2+"
(f-overlap-mfa 1 2) ;=> 2
(f-overlap-mfa 1 2 3) ;=> "2+"

So the two argument result is different for me than you, Francis Avila.

The call with just one argument does give a warning though:

WARNING: Wrong number of args (1) passed to cljs.user/f-overlap-mfa





[CLJS-1677] Requiring [goog] breaks an :advanced build, but the compiler exits successfully Created: 09/Jun/16  Updated: 10/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.36
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Daniel Compton Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

A single file with the following in it is enough to break a build:

(ns goog-error.core
  (:require [goog]))

with this error

Jun 10, 2016 11:18:03 AM com.google.javascript.jscomp.LoggerErrorManager println
SEVERE: ERROR - Duplicate input: file:/Users/danielcompton/.m2/repository/org/clojure/google-closure-library/0.0-20151016-61277aea/google-closure-library-0.0-20151016-61277aea.jar!/goog/base.js

Jun 10, 2016 11:18:03 AM com.google.javascript.jscomp.LoggerErrorManager printSummary
WARNING: 1 error(s), 0 warning(s)
ERROR: JSC_DUPLICATE_INPUT. Duplicate input: file:/Users/danielcompton/.m2/repository/org/clojure/google-closure-library/0.0-20151016-61277aea/google-closure-library-0.0-20151016-61277aea.jar!/goog/base.js at (unknown source) line (unknown line) : (unknown column)

however the ClojureScript compiler exits successfully without throwing an error. The build looks successful, but the file produced doesn't work. Should the ClojureScript compiler throw on these kinds of errors, or otherwise indicate failure?



 Comments   
Comment by David Nolen [ 10/Jun/16 8:27 AM ]

We should look into why the namespace validation that checks where a ns exists or not isn't already catching this case.





[CLJS-1665] Use Javascript array to create a collection in cljs.reader Created: 03/Jun/16  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Rohit Aggarwal Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: performance

Attachments: Text File CLJS-1665.patch    
Patch: Code

 Description   

For performance, we should be using a Javascript array to create an ArrayMap/HashMap/Set/List/Vector instead of creating them out of a Vector. Most of the underlying data types do have methods to convert from an array to that type.

The big change is that cljs.reader/read-delimited-list now returns an array instead of a vector.

Benchmarking code:

(def nums (range 20))
(def nums-map (into {} (map (fn [i] [i i]) nums)))

(simple-benchmark [s "{:foo 1 :bar 2}"] (reader/read-string s) 1000000)
(simple-benchmark [s (pr-str nums-map)] (reader/read-string s) 100000)
(simple-benchmark [s (pr-str nums)] (reader/read-string s) 100000)
(simple-benchmark [s (pr-str (vec nums))] (reader/read-string s) 100000)
(simple-benchmark [s (pr-str (set nums))] (reader/read-string s) 100000)

Results (All times in msecs):

Engine Benchmark Old New Improvement
v8 Small Map 6620 5516 20.01%
SpiderMonkey Small Map 11929 10606 12.47%
JSC Small Map 5101 4158 22.68%
v8 Large Map 6075 5548 9.50%
SpiderMonkey Large Map 13070 11933 9.53%
JSC Large Map 4777 4273 11.79%
v8 List 2308 2280 1.23%
SpiderMonkey List 6167 4777 29.10%
JSC List 1891 1737 8.87%
v8 Vector 2276 2242 1.52%
SpiderMonkey Vector 5239 4700 11.47%
JSC Vector 1832 1684 8.79%
v8 Set 3362 3271 2.78%
SpiderMonkey Set 7283 6880 5.86%
JSC Set 2771 2619 5.80%





[CLJS-1658] implements? may report false positives Created: 01/Jun/16  Updated: 02/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Thomas Heller Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File implements-false-positives.patch     Text File implements-false-positives-with-sentinel.patch    
Patch: Code

 Description   

The cljs.core/implements? checks whether a protocol is implemented via a property on the tested object. When implementing the protocol this property will be set to true.

// the implementation
protocol.check.Dummy.prototype.protocol$check$TheProtocol$ = true;

// the check (implements? TheProtocol x)
((!((x == null)))?(((false) || (x.protocol$check$TheProtocol$))?true:false):false)

// only the relevant bit
x.protocol$check$TheProtocol$

This works fine under :none but :advanced may rename this to something like x.A. If you now try to check (implements? TheProtocol js/window) for example it may (or may not) report a false positive. For larger projects the likelyhood of creating a false positives goes way up since window contains all the variables created by the advanced compiled js.

The attached patch changes the patch the emitted code to check x.protocol$check$TheProtocol$ === true which reduces the chance of a false positive by a lot. We might chose another sentinel value instead to reduce the chance some more, this seems good enough though.



 Comments   
Comment by Thomas Heller [ 01/Jun/16 2:13 PM ]

Implemented the same change for cljs.core/satisfies?.

Comment by Thomas Heller [ 01/Jun/16 2:18 PM ]

Benchmark on my MBP indicate that the change comes with a slight performance cost.

// v8 with patch
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 10 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 27 msecs

//v8 without patch
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 8 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 19 msecs
Comment by Thomas Heller [ 01/Jun/16 2:28 PM ]
// spidermonkey with patch
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 68 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 146 msecs

// spidermonkey without patch
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 63 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 149 msecs
Comment by Thomas Heller [ 01/Jun/16 2:35 PM ]

JavascriptCore

// jsc with path
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 7 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 14 msecs

// jsc without patch
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 6 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 13 msecs
Comment by Thomas Heller [ 02/Jun/16 3:40 AM ]

Added a second patch that uses a sentinel value as the protocol marker. Currently just a plain empty javascript object.

Running the benchmarks shows no real difference to just checking "true".

I also tried using a number or string as the sentinel value but could not find any significant difference either.

// emitted code on protocol impls
protocol.check.Dummy.prototype.protocol$check$TheProtocol$ = cljs.core.PROTOCOL_SENTINEL;

// the check (identical?)
cljs.core.PROTOCOL_SENTINEL === x.protocol$check$TheProtocol$




[CLJS-1657] Self-host: Implicit macro loading with alias Created: 01/Jun/16  Updated: 01/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: bootstrap


 Description   

If a namespace relies on implicit macro loading (described here https://github.com/clojure/clojurescript/wiki/Differences-from-Clojure#namespaces), and an alias is used, it is possible for aliased symbol resolution to fail.

This can be reproduced by adding a 10th case in self-host.test/test-load-and-invoke-macros covering this situation:

(let [st (cljs/empty-state)]
        ;; Rely on implicit macro loading (ns loads its own macros), with an alias
        (cljs/eval-str st
          "(ns cljs.user (:require [foo.core :as foo]))"
          nil
          {:eval node-eval
           :load (fn [{:keys [macros]} cb]
                   (if macros
                     (cb {:lang :clj :source "(ns foo.core) (defmacro add [a b] `(+ ~a ~b))"})
                     (cb {:lang :clj :source "(ns foo.core (:require-macros foo.core))"})))}
          (fn [{:keys [value error]}]
            (is (nil? error))
            (cljs/eval-str st
              "(foo/add 300 500)"
              nil
              {:eval    node-eval
               :context :expr}
              (fn [{:keys [error value]}]
                (is (nil? error))
                (is (= 800 value))
                (inc! l))))))

This will result in:

Testing self-host.test

FAIL in (test-load-and-invoke-macros) (at .../clojurescript/builds/out-self/core-self-test.js:11545:454)
expected: (nil? error)
  actual: (not (nil? #error {:message "ERROR", :data {:tag :cljs/analysis-error}, :cause #object[TypeError TypeError: Cannot read property 'call' of undefined]}))

FAIL in (test-load-and-invoke-macros) (at .../clojurescript/builds/out-self/core-self-test.js:11548:49)
expected: (= 800 value)
  actual: (not (= 800 nil))


 Comments   
Comment by Mike Fikes [ 01/Jun/16 7:48 AM ]

Analysis: This is because, this bit of code https://github.com/clojure/clojurescript/blob/19510523ad9de3f16832d287bae86f701e8b4263/src/main/clojure/cljs/analyzer.cljc#L1817-L1820
has a branch that only works in non-bootstrap ClojureScript.

You can also work around the issue (or gain a better understanding) in several ways (if you can control the affected loading namespace—this won't work if this affects code down in a library you are loading):

1. You can add :include-macros true
2. You can load the self-macro-loading namespace twice. (For example, if at the REPL, you can (require [foo.core :as foo]) twice.) This causes the analysis state to be set up so that on the second require, it is taken care of in the first clause of the or in the linked code above.
3. You can even assoc-in enough state prior to the load so that you are effectively doing (2).

In all of these cases, when it fails, vs. when it succeeds, you can see a difference in the :require-macros key in the analysis map of the loading namespace ('cljs.user, for example). When it fails, you will see that this key is empty and when it succeeds, you will see that the key is populated, and in particular, with the foo alias.

In the case where you don't use an alias, implicit macro loading will fail in a way that won't be visible: The :require-macros key will not be set up as described above, but qualified symbol resolution will still succeed (foo.core/add in the example will resolve to foo.core$macros/add), probably simply owing to the way resolution is performed.





[CLJS-1651] Self-host: Cannot replace core macro-function Created: 28/May/16  Updated: 28/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bootstrap


 Description   

Replace double to multiply by two in self host and you will see that operator-position resolution chooses the core macro:

ClojureScript Node.js REPL server listening on 54425
To quit, type: :cljs/quit
cljs.user=> (require 'cljs.js)
nil
cljs.user=> (def st (cljs.js/empty-state))
#'cljs.user/st
cljs.user=> (cljs.js/eval-str st "(defn double [x] (* 2 x))" nil {:eval cljs.js/js-eval :context :expr} identity)
WARNING: double already refers to: cljs.core/double being replaced by: cljs.user/double at line 1
{:ns cljs.user, :value #object[cljs$user$double "function cljs$user$double(x){
return ((2) * x);
}"]}
cljs.user=> (cljs.js/eval-str st "[(double 3) (apply double [3])]" nil {:eval cljs.js/js-eval :context :expr} identity)
{:ns cljs.user, :value [3 6]}

The correct result above would be [6 6].






[CLJS-1646] Internal compiler error when importing Showdown via CommonJS Created: 24/May/16  Updated: 26/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Juan Pedro Bolivar Puente Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

cljs 1.8.51, clojure 1.8, lein-cljsbuild 1.1.3



 Description   

Hi!

I am trying to use Showdown 1.4.1 from NPM [1] using a `:commonjs` foreign-lib. Sadly, compilation crashes with the following error:

java.lang.RuntimeException: INTERNAL COMPILER ERROR.
Please report this problem.

null
Node(SCRIPT): node_modules/showdown/dist/showdown.js:1:0
[source unknown]
Parent: NULL
at com.google.javascript.jscomp.ES6ModuleLoader.normalizeInputAddress(ES6ModuleLoader.java:118)
at com.google.javascript.jscomp.ProcessCommonJSModules.inputToModuleName(ProcessCommonJSModules.java:89)
at com.google.javascript.jscomp.ProcessCommonJSModules$ProcessCommonJsModulesCallback.visitScript(ProcessCommonJSModules.java:336)
at com.google.javascript.jscomp.ProcessCommonJSModules$ProcessCommonJsModulesCallback.visit(ProcessCommonJSModules.java:245)
at com.google.javascript.jscomp.NodeTraversal.traverseBranch(NodeTraversal.java:623)
at com.google.javascript.jscomp.NodeTraversal.traverse(NodeTraversal.java:297)
at com.google.javascript.jscomp.NodeTraversal.traverseEs6(NodeTraversal.java:564)
at com.google.javascript.jscomp.ProcessCommonJSModules.process(ProcessCommonJSModules.java:85)
at cljs.closure$eval5486$fn__5487.invoke(closure.clj:1541)
at clojure.lang.MultiFn.invoke(MultiFn.java:233)
at cljs.closure$write_javascript.invokeStatic(closure.clj:1601)
at cljs.closure$write_javascript.invoke(closure.clj:1578)
at cljs.closure$source_on_disk.invokeStatic(closure.clj:1624)
at cljs.closure$source_on_disk.invoke(closure.clj:1619)
at cljs.closure$output_unoptimized$fn__5536.invoke(closure.clj:1662)
at clojure.core$map$fn__4785.invoke(core.clj:2646)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:521)
at clojure.core$seq__4357.invokeStatic(core.clj:137)
at clojure.core$filter$fn__4812.invoke(core.clj:2700)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:521)
at clojure.core$seq__4357.invokeStatic(core.clj:137)
at clojure.core$map$fn__4785.invoke(core.clj:2637)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.Cons.next(Cons.java:39)
at clojure.lang.RT.next(RT.java:688)
at clojure.core$next__4341.invokeStatic(core.clj:64)
at clojure.core$str$fn__4419.invoke(core.clj:546)
at clojure.core$str.invokeStatic(core.clj:544)
at clojure.core$str.doInvoke(core.clj:533)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invokeStatic(core.clj:646)
at clojure.core$apply.invoke(core.clj:641)
at cljs.closure$deps_file.invokeStatic(closure.clj:1340)
at cljs.closure$deps_file.invoke(closure.clj:1337)
at cljs.closure$output_deps_file.invokeStatic(closure.clj:1360)
at cljs.closure$output_deps_file.invoke(closure.clj:1359)
at cljs.closure$output_unoptimized.invokeStatic(closure.clj:1670)
at cljs.closure$output_unoptimized.doInvoke(closure.clj:1653)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$apply.invoke(core.clj:641)
at cljs.closure$build.invokeStatic(closure.clj:1981)
at cljs.closure$build.invoke(closure.clj:1882)
at cljs.build.api$build.invokeStatic(api.clj:210)
at cljs.build.api$build.invoke(api.clj:198)
at cljs.build.api$build.invokeStatic(api.clj:201)
at cljs.build.api$build.invoke(api.clj:198)
at cljsbuild.compiler$compile_cljs$fn__5771.invoke(compiler.clj:60)
at cljsbuild.compiler$compile_cljs.invokeStatic(compiler.clj:59)
at cljsbuild.compiler$compile_cljs.invoke(compiler.clj:48)
at cljsbuild.compiler$run_compiler.invokeStatic(compiler.clj:168)
at cljsbuild.compiler$run_compiler.invoke(compiler.clj:122)
at user$eval5908$iter_59445948$fn5949$fn_5967.invoke(form-init8819033363931377476.clj:1)
at user$eval5908$iter_59445948$fn_5949.invoke(form-init8819033363931377476.clj:1)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:521)
at clojure.core$seq__4357.invokeStatic(core.clj:137)
at clojure.core$dorun.invokeStatic(core.clj:3024)
at clojure.core$doall.invokeStatic(core.clj:3039)
at clojure.core$doall.invoke(core.clj:3039)
at user$eval5908.invokeStatic(form-init8819033363931377476.clj:1)
at user$eval5908.invoke(form-init8819033363931377476.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6917)
at clojure.lang.Compiler.load(Compiler.java:7379)
at clojure.lang.Compiler.loadFile(Compiler.java:7317)
at clojure.main$load_script.invokeStatic(main.clj:275)
at clojure.main$init_opt.invokeStatic(main.clj:277)
at clojure.main$init_opt.invoke(main.clj:277)
at clojure.main$initialize.invokeStatic(main.clj:308)
at clojure.main$null_opt.invokeStatic(main.clj:342)
at clojure.main$null_opt.invoke(main.clj:339)
at clojure.main$main.invokeStatic(main.clj:421)
at clojure.main$main.doInvoke(main.clj:384)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
... 85 more

[1] https://www.npmjs.com/package/showdown



 Comments   
Comment by Juan Pedro Bolivar Puente [ 24/May/16 12:39 PM ]

Forgot to mention, this is the line in my :foreign-libs

{:file "node_modules/showdown/dist/showdown.js"
:provides ["showdown"]
:module-type :commonjs}

Same problem occurs if I use showdown.min.js





[CLJS-1644] Recursive protocol method for a record never terminates Created: 23/May/16  Updated: 26/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Brian Stiles Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Any



 Description   

When extending a protocol for a record, a recursive method can never terminate in some cases.

(defrecord R [a])

(defprotocol P
  (f [x]))

(extend-protocol P
  R
  (f [x]
    (if x
      (recur nil)
      x))
  default
  (f [x]
    (if x
      (recur nil)
      x)))

(prn (f 1))        ; #1
(prn (f (R. 1)))   ; #2

prn call #1 prints nil as expected, but prn call #2 never terminates.

It looks like the compiler creates a variable assigned to this within the while loop such that the test of "x" is always really testing this when it should be testing the value of x passed in by the call to recur.

Note, I'm testing ClojureScript 1.8.51. The "Affects Version/s" field above only gives 1.7.228 as the most recent version.



 Comments   
Comment by Brian Stiles [ 23/May/16 4:14 AM ]

Actually, "always really testing 'this' when it should be testing the value of x passed in by the call to recur" is only true if the type of the value represented by x remains the same. If, as in the example, the type changes, the call should be dispatched to the properly matching method implementation.

This seems to behave as expected in Clojure.





[CLJS-1643] Emit more informative error when emitting a type which has no emit multimethod case Created: 21/May/16  Updated: 21/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Users may accidentally splice in Clojure functions and other things in macros that work in Clojure which cannot work in ClojureScript. We may want to include a default warning for the most common/likely error cases.






[CLJS-1641] Multi-arity defn copies arguments unnecessarily for all cases Created: 16/May/16  Updated: 12/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.36
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Stephen Brady Assignee: Stephen Brady
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1641.patch    
Patch: Code

 Description   

Background:

Passing js arguments as a parameter to another function is a known performance issue. Copying arguments to an array addresses this, and this approach has been taken to handle args passing for variadic functions in previous patches such as:
https://github.com/clojure/clojurescript/commit/dccc5d0feab9809cb56bd4d7257599a62232ac0d
https://github.com/clojure/clojurescript/commit/f09bbe62e99e11179dec6286fbb46265c12f4737

This commit (https://github.com/clojure/clojurescript/commit/576fb6e054dd50ec458a3c9e4172a5a0002c7aea) introduced a macro path for `defn` forms.

Current Behavior and Impact:

In the case of multi-arity defn forms, the macro path generates an array copy of the arguments variable regardless of whether it is used or necessary. In the case of multiple arities but no variadic arity, copying arguments is unnecessary as arguments will not be passed to the variadic method for the given function. In the case of multiple arities with a variadic case, an args array copy is needed but should be isolated to that case alone; currently, the array copy is performed before checking the arguments length, causing all cases to incur an (unused) args array copy.

Relevant code: https://github.com/clojure/clojurescript/blob/master/src%2Fmain%2Fclojure%2Fcljs%2Fcore.cljc#L2827-L2843

Recommended Change:

  • Do not perform an args array copy before switching on arguments length
  • Perform an args array copy within the variadic dispatch case

Patch forthcoming.



 Comments   
Comment by David Nolen [ 21/May/16 5:03 PM ]

Thanks! Will take a look.

Comment by David Nolen [ 10/Jun/16 1:32 PM ]

This probably needs to be updated in the compiler as well.

Comment by Stephen Brady [ 10/Jun/16 2:43 PM ]

The compiler already isolates the args to array copying behavior in the variadic case. The unnecessary copying is isolated to the defn macro.

These are the only two calls to `emit-arguments-to-array`:

Variadic function: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/compiler.cljc#L743
Multi-arity with variadic case: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/compiler.cljc#L812

Comment by Francis Avila [ 11/Jun/16 8:33 AM ]

From what I can see copy-arguments is only ever used with an immediately-constructed empty array as the dest in the pattern:

`(let [arr# (array)]
  (copy-arguments arr#)
  ;;...
  )

Perhaps it should change to have the exact same behavior as emit-arguments-to-array, i.e. it should take a start index and expand to the name of the destination array. The advantages of this approach are 1) it can preallocate the array to the correct size and 2) your patch no longer needs a slice call--you can avoid allocating two arrays instead of just one. (These two reasons are why I implemented emit-arguments-to-array the way I did.)

I can't think of a way to implement emit-arguments-to-array as a macro without emitting a wrapping js function which messes up the scope, but you could do this:

(defmacro copy-arguments [dest-arr startslice]
  `(loop [i# 0]
     (when (< i# (alength ~dest-arr))
       (aset ~dest-arr i# (aget (js-arguments) (+ i# startslice)))
       (recur (inc i#)))))

And then at the callsite:

`(let [variadic-args-arr# (make-array (- (alength (js-arguments)) ~maxfa))]
  (copy-arguments variadic-args-arr# ~maxfa)
    (let [argseq# (new ^::ana/no-resolve cljs.core/IndexedSeq
                       variadic-args-arr# 0 nil)]
      ;; ...
    ))

However, there are some bugs around arity handling that the above would not solve: CLJS-1678

Comment by Stephen Brady [ 11/Jun/16 8:27 PM ]

Francis, thanks for commenting on this. The patch that I submitted simply moves where/when `copy-arguments` is called. Other than that, I preserved all other aspects of the existing implementation, including how the array is built up and then made into an IndexedSeq. The line diffing in the patch implies that I changed a lot more than I really did. Agreed with your point that `emit-arguments-to-array` is more efficient and precise. Intentionally, I did not try to alter/improve/correct anything in this patch beyond solving the objective in the issue: not unnecessarily copying arguments.

Glad you've reported CLJS-1678 as I've observed this too. This buggy behavior shows up in several places. Beyond what this issue-patch attempts to address, in general, my observation is that we could probably clear out some under-brush that's accumulated as the compiler has matured with regards to arguments handling and code generated for multi-arity and/or variadic functions, apply / applyTo, and implementations of IFn. Seems like there are several opportunities to emit less javascript, create fewer intermediates, and shorten the call chain.

So to reiterate, this patch - despite its superficial appearance - changes very little and just moves the call to copy-arguments to the appropriate place. The benefit is:

For multi-arity functions with no variadic arity, no code for copying the arguments to an array is emitted at all (which in aggregate turns out to be a decent amount). Obviously, at runtime, no array will be created.

For multi-arity functions with a variadic arity, the code for copying the arguments remains but is isolated to the variadic case, and so if the function is called but will dispatch to one of the fixed arities, again, no array will be created.





[CLJS-1639] Invalid file path for cljs.core.load_file on windows Created: 13/May/16  Updated: 21/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Jay Lee Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug
Environment:

Windows 10, CLJS 1.8.51



 Description   

When I tried to build reagent based app with nodejs target,
I got an invalid file path generated case which is basically loading external javascript file.
I captured the image as following:

At line 4, the path should have double backward-slash on windows.

I has built a CLJS app which is based on reagent framework with nodejs target. The build environment is somewhat strange but I have a case to use it. Here is a reproduce steps.

  1. Open command prompt on windows 10 and execute command as following:
    lein new figwheel sample00 – --reagent
  2. Open project.clj file and update one of dependencies:
    project.clj
    ;; ...
    :dependencies [[org.clojure/clojure "1.8.0"]
                     [org.clojure/clojurescript "1.8.51"]
                     [org.clojure/core.async "0.2.374"
                      :exclusions [org.clojure/tools.reader]]
    
                     [reagent "0.6.0-alpha" :exclusions [cljsjs/react]]
                     [cljsjs/react-with-addons "0.14.3-0"]
                     ]
    
    ;; ...
    :cljsbuild {:builds [{:id "dev"
                          ;; ... 
                          :compiler {
                            ;; ...
                            :target :nodejs
                          }
                         }]
               }
    ;; ...
  3. Build with lein cljsbuild once dev
  4. Open <project_root>\resources\public\js\compiled\out\reagent\impl\util.js
  5. At line number 4 in my environment, the generated code is
    cljs.core.load_file("resources\public\js\compiled\out\react-with-addons.inc.js");
    However I believe the correct path string should be cljs.core.load_file("resources\\public\\js\\compiled\\out
    react-with-addons.inc.js");
    .

Backward-slash needs to be double on Windows env.
When I lunched doo test command with nodejs target, it complained about the given path cannot be loaded.

Thanks.



 Comments   
Comment by David Nolen [ 21/May/16 5:07 PM ]

This ticket is in danger of being closed. The ticket should demonstrate a reproducible bug without relying on any 3rd party tools or libraries. No Leiningen, Figwheel, or Reagent. Please demonstrate the Windows issue with only ClojureScript.

Thanks.





[CLJS-1636] Mark some symbols in core macros ns as private Created: 27/Apr/16  Updated: 27/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1636.patch    
Patch: Code

 Description   

There are some symbols in the core macros namespace that are not meant for external consumption. Some of these are marked private and some aren't. This ticket asks that the others be marked private as well.

An example of one symbol marked private is defcurried.
An example of one symbol not marked private is caching-hash.



 Comments   
Comment by Mike Fikes [ 27/Apr/16 8:21 AM ]

In CLJS-1636.patch, I checked and it appears nothing in the compiler codebase is explicitly using these symbols outside of the cljs.core namespace. But, it is still worth scanning through these to check if they make sense. For example js-debugger and js-comment are a couple that might actually be meant for public use, but it is difficult to tell.

Comment by Mike Fikes [ 27/Apr/16 2:43 PM ]

Note, that in #cljs-dev slack, there appears to be interest in caching-hash being public.

(I don't mind revising the patch to suit whatever is needed. At the same time, I'm certainly not in a position to take decisions on what is public API or not.)

Comment by Mike Fikes [ 27/Apr/16 2:43 PM ]

Note, that in #cljs-dev slack, there appears to be interest in caching-hash being public.

(I don't mind revising the patch to suit whatever is needed. At the same time, I'm certainly not in a position to take decisions on what is public API or not.)





[CLJS-1634] Track bound dynamic variables to support binding in async mechanisms. Created: 26/Apr/16  Updated: 27/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Christian Weilbach Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: cljs, enhancement
Environment:

Any cljs version.



 Description   

The issue has been raised before:

While the reasoning behind the proposal is still valid, the original approach has made no progress due to the performance penalty. I have implemented a simplified approach with mutable JavaScript datastructures to minimize the performance impact. Because we are single-threaded we can use js assignment and don't need to port Clojure's binding frame. A small penalty is paid by the user of binding (see benchmark8) and a higher one by async mechanisms capturing and restoring the bindings (benchmark1-7):

https://gist.github.com/whilo/a8ef2cd3f0e033d3973880a2001be32a

I would provide patches to ClojureScript, if this looks like a worthwhile approach.



 Comments   
Comment by Antonin Hildebrand [ 30/Apr/16 6:05 AM ]

Just for record I commented on it here: https://gist.github.com/whilo/a8ef2cd3f0e033d3973880a2001be32a#gistcomment-1764489. Not sure if GitHub sends out notifications about new gist comments.

Comment by Christian Weilbach [ 30/Apr/16 6:18 AM ]

Thanks for pointing it out. David Nolen has also pointed out prototype chains to address this issue and now I see what he meant. I am not familiar enough with the internals of "this" in JavaScript, but one problem I see is that you need to distinguish dynamic vars at the call site. The advantage of using an object directly and capturing and restoring the frame explicitly when you enter and leave the code is that call sites are totally unaffected. The cost is only paid a little at the binding site and mostly in async libraries (bound-fn). But I might still need to look further into "this" . I have not got the gist comment from github.

Comment by Antonin Hildebrand [ 30/Apr/16 7:23 AM ]

Correct.

> you need to distinguish dynamic vars at the call site

I agree. My initial motivation was to solve a bit different problem without cooperation from library authors. I didn't want to modify ClojureScript behaviour and wanted to be just touching own code or doing trivial changes in library forks. Just wanted to share my thoughts about the implementation.

I have a feeling that solving this "async context" problem will be difficult. You will need async library authors to adapt their libraries. And users unaware of this will be running into issues anytime they step outside of bound-fn aware async libraries (for example using raw js interop). I believe Angular people solved this robustly in https://github.com/angular/zone.js. The implementation is quite scary monkey-patching, but if they were able to wrap all existing async calls at lowest level, maybe we could just build on top of their foundation and use zone.js as parallel mechanism for `binding`.

Comment by Christian Weilbach [ 02/May/16 4:58 PM ]

The angle I am coming from is roughly described here: https://github.com/fullcontact/full.monty/pull/9#issuecomment-131152058

I only found out at the very end when I had supervision of go-channels completely implemented, that the cljs binding was not behaving like the Clojure one. Arguments pro/contra binding in Clojure are also referenced. The zone monkey patching looks very heavy and prone to cause hairy bugs. It is noteworthy that Clojure does not embrace bindings, but keeps them always thread-local. So there you also have to use bound-fn or something similar whenever code is executed concurrently. core.async for instance uses the Clojure mechanism to push (capture) and pop (restore) bindings. I would like to have this in ClojureScript as well. I think one should not retain all bindings automatically, but rather allow the library author to handle dynamic bindings. I only track the supervisor binding for instance. For ClojureScript as for Clojure libraries and wrappers this should be fine. Pure JavaScript libraries usually have their own binding concepts like zone.js, right?

Tracking bindings is neither for free in Clojure nor in ClojureScript and it is an important design goal to embrace the host. In fact originally I tried to capture and restore all bindings. My benchmarks for tracking more dynamic vars (instead of just the currently active binding), were linearly more expansive than rebinding fewer selected vars and become prohibitive when you reach a few hundred.

Comment by Antonin Hildebrand [ 02/May/16 5:16 PM ]

I would be happy if your proposal went through. It would help my use-cases as well.

I'm going to explore zone.js when I get some spare time. I will try do write a wrapper library and implement an alternative mechanism to bindings using zone.js. I would like to provide this functionality as a library without a need to modify ClojureScript compiler or involvement from library authors.

Comment by Christian Weilbach [ 03/May/16 1:39 AM ]

Ok, I am curious how well this will work. Would this work with the state-machine transformation of core.async?

Comment by Antonin Hildebrand [ 03/May/16 3:25 AM ]

I believe so. Core.async state machine uses only setTimeout and goog.async.nextTick. We can teach zone.js to deal with nextTick by setting goog.async.nextTick.wrapCallback_ with zone wrapping. Also if user decided to use any async API in their go blocks it should work, because zone.js will carry proper zone binding over async boundaries.

Comment by Antonin Hildebrand [ 03/May/16 3:50 AM ]

I have opened this issue in zone.js: https://github.com/angular/zone.js/issues/342

Comment by Christian Weilbach [ 26/May/16 8:56 AM ]

Hey. Have you made any progress with implementing a small cljs demo with zone.js yet?

Comment by Antonin Hildebrand [ 27/May/16 5:57 AM ]

Hi Christian. No, unfortunately I didn't get to it.





[CLJS-1633] Improve error associated with invalid foreign-libs :file path Created: 26/Apr/16  Updated: 06/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Oliver George Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

The current error reported when :advanced compiling with an invalid :foreign-libs :file path is effectively (slurp nil):

Caused by: java.lang.IllegalArgumentException: Cannot open <nil> as a Reader.

With a small patch this could be improved to provide a specific message and relevant context, something like:

Caused by: clojure.lang.ExceptionInfo: Unable to resolve url for foreign-deps source {:foreign true, :url nil, :source-url nil, :provides ["cljsjs.example-thing"], :requires (), :lines nil, :source-map nil, :file "broken-path-to-example-thing.js"}

I've created a simple repo based on the mies template to demonstrate the problem. Note that core.cljs requires the foriegn-lib which is defined in deps.clj (but with an invalid :file path). scripts/release.clj shows current behaviour. scripts/release-with-patch.clj shows proposed behaviour.

https://github.com/condense/apr26-foreign-libs-path-error.core.git

Below shows an isolated fix to cljs.closure/foreign-deps-str which checks for a nil url. An alternative approach might be to check at the point where the source maps are prepared (something like (assert (or url url-min) "xxx")) but this would be more likely to have side effects.

(defn foreign-deps-str [opts sources]
  (letfn [(to-js-str [ijs]
            (if-let [url (or (and (#{:advanced :simple} (:optimizations opts))
                                  (:url-min ijs))
                             (:url ijs))]
              (slurp url)
              (throw (ex-info "Unable to resolve url for foreign-deps source" ijs))))]
    (str (string/join "\n" (map to-js-str sources)) "\n")))

I'd be happy to prepare a patch if this seems like the right approach. Have signed contributor agreement.



 Comments   
Comment by David Nolen [ 06/May/16 1:56 PM ]

A patch for this small enhancement is welcome.





[CLJS-1631] The str function should handle JavaScript symbols Created: 21/Apr/16  Updated: 21/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Roman Scherer Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

The str function should handle primitive JavaScript symbols produced by Symbol.for. At the moment the str function raises an exception, because it runs into some JavaScript safety checks by using implicit string coersion via (.join #js [x] "").
More info on the safety check here: http://www.2ality.com/2014/12/es6-symbols.html
This ticket is also related to:

(def x (.for js/Symbol "x"))
(str x)
TypeError: Cannot convert a Symbol value to a string
    at Array.join (native)
    at Function.cljs.core.str.cljs$core$IFn$_invoke$arity$1 (/home/roman/workspace/clojurescript/.cljs_node_repl/cljs/core.js:9561:12)
    at Object.cljs$core$str [as str] (/home/roman/workspace/clojurescript/.cljs_node_repl/cljs/core.js:9543:22)
    at repl:1:100
    at repl:9:3
    at repl:14:4
    at Object.exports.runInThisContext (vm.js:54:17)
    at Domain.<anonymous> ([stdin]:41:34)
    at Domain.run (domain.js:228:14)
    at Socket.<anonymous> ([stdin]:40:25)

Calling the toString method on a symbol directly works

(.toString x)
;;=> "Symbol(x)"





[CLJS-1630] Add unit test for static dispatch Created: 21/Apr/16  Updated: 23/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Yehonathan Sharvit Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1630.patch    

 Description   

This unit test is an edge case that illustrates why in the code of `emit :invoke` we must stay with `call` for the high order case where static information is missing .






[CLJS-1628] Make instances of js/Symbol printable Created: 20/Apr/16  Updated: 21/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Roman Scherer Assignee: Roman Scherer
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1628.patch    
Patch: Code and Test

 Description   

At the moment instances of js/Symbol are not printable. Trying to
print them or enter them in the REPL causes an exception.

cljs.user> (.for js/Symbol "react.element")
#object[TypeError TypeError: Cannot convert a Symbol value to a string]

Symbols are supported in all major browsers, except Internet Explorer and Nashorn.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol



 Comments   
Comment by Roman Scherer [ 20/Apr/16 10:23 AM ]

The attached patch adds an additional clause to `pr-writer-impl` and
implements the printing of Symbol instances, in the same way as it is
done for other JavaScript objects. Here's an example of a printed
js/Symbol:

(.for js/Symbol "react.element")
;;=> #object[Symbol "react.element"]

@david: Regarding your point about shims, do you think the
implementation of `js-symbol-defined?`, which was used for the ES6
iterator support, is enough for this patch? I'm not too familiar with
JavaScript and not sure if this already addressed the "shim" issue.

Another thing I stumbled upon is, that my test currently generates a
compiler warning when using the default compiler options. The warning
is generated when compiling the following ClojureScript form:

(.for js/Symbol "react.element")

The following snippet shows the warning and the generated code from my
test:

WARNING - Keywords and reserved words are not allowed as unquoted
property names in older versions of JavaScript. If you are targeting
newer versions of JavaScript, set the appropriate language_in option.

try{var values__13328__auto__ = (function (){var x__6628__auto__ = cljs.core.pr_str.cljs$core$IFn$_invoke$arity$variadic(cljs.core.array_seq([Symbol.for("react.element")], 0));

I think this has nothing to do with this patch, but with the emitted
code not being legal Ecmascript 3, since "for" is a reserved word.

The warning goes away when changing the :language-in option to
something newer than Ecmascript 3, or doing something like this:

((gobj/get js/Symbol "for") "react.element")

So, the questions is: Should the ClojureScript compiler handle those
reserved words when they appear in a function call or property lookup?

If that's the case I would leave the warning in that patch, and open
another issue for this new problem.

What do you think?

Roman

Comment by Roman Scherer [ 20/Apr/16 11:33 AM ]

Ok, given advanced mode and renaming of symbols I think there's not much the ClojureScript compiler can do about this.
I think I'll change the test to use ((gobj/get js/Symbol "for") "react.element") to remove the warning.
https://github.com/google/closure-compiler/wiki/FAQ#i-get-invalid-property-id-errors-but-it-works-on-firefox

Comment by David Nolen [ 20/Apr/16 2:21 PM ]

ECMAScript 3 is not an issue for people who are ok with outputting ECMAScript 5. The goog.typeOf of check on "symbol" does not seem like it would work with Symbol shims, have you confirmed?

Comment by Roman Scherer [ 21/Apr/16 10:57 AM ]

David, yes you are right about goog.typeOf, it returns "object" when
js/Symbol is implemented via shims. I updated the patch and tested it
against the following shim implementation in Nashorn so far.

Start a Nashorn REPL.

./script/nashornrepljs

js/Symbol does not exist in Nashorn yet.

(exists? js/Symbol)
;;=> false

Load core-js shim into Nashorn.

(js/load "https://raw.githubusercontent.com/zloirock/core-js/master/client/shim.min.js")
(print (.for js/Symbol "x"))
;;=> #object[Symbol "x"]

I gave up on this shim implementation because I got an error when
trying to load the minified js into Nashorn.

https://github.com/medikoo/es6-symbol

This is another shim implementation, but it only enhances js/Symbol
when it already exists, so not useful in the context of Nashorn.

https://github.com/paulmillr/es6-shim

Comment by Roman Scherer [ 21/Apr/16 2:07 PM ]

Which implementations do support js/Symbol?

JavaScriptCore, NodeJS, SpiderMonkey, Chrome and Firefox support
js/Symbol. Evaluating (instance? js/Symbol (.for js/Symbol "x")) on
those engines returns false. On all those implementations symbols
generated via (.for js/Symbol "x") are primitive values and can be
identified via typeof. See the "Using the typeof operator with
symbols" section in [1].

Nashorn and Rhino do not support js/Symbol.

[1] https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol

Comment by Roman Scherer [ 21/Apr/16 2:27 PM ]

Looking around I found that the exception caused by printing symbols
happens because the str [1] function uses implicit string conversion
to convert it's argument into a string. This is explained in the
"Coercing a symbol to string" section of [2]. One way to solve the
problem is to use (.toString x) instead.

(.join #js [(.for js/Symbol "x")] "")
;;=> TypeError: Symbol is not a constructor

(.toString (.for js/Symbol "x"))
;;=> "Symbol(x)"

[2] https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L2695
[2] http://www.2ality.com/2014/12/es6-symbols.html

Comment by Roman Scherer [ 21/Apr/16 2:38 PM ]

(= (goog/typeOf x) "symbol") vs (instance? js/Symbol x)

Symbols are primitive types and must be constructed via
Symbol("x"). They can't be constructed via new Symbol("x"), trying
to do so raises a TypeError. This is explained in more detail in the
"Safety checks" section of [1]. The "Using the typeof operator with
symbols" section of [2] explains how to identify symbols. It has to be
done via typeof, because symbols are primitive types and not
instances of Symbol.

[1] http://www.2ality.com/2014/12/es6-symbols.html
[2] https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol

Comment by Roman Scherer [ 21/Apr/16 3:02 PM ]

I think we have 2 options now to fix exceptions when printing symbols:

  • Change str to handle symbols as well.
  • Implement custom printing for symbols.

I think I would go for the first option. I think calling str on a
symbol can't be seen as implicitly coercing a symbol to a string, I
would say this is as explicit as it can get and we don't have to raise
an exception in this case. I think str is basically the Clojure
equivalent to toString in JavaScript, at least this is what the out
of date doc string of str says.

The file CLJS-1628-fix-str has an implementation of the first option.

Comment by Francis Avila [ 21/Apr/16 3:35 PM ]

Changing str to use .toString is not possible because of CLJS-847: there are unconfirmable reports that .toString() causes TypeError on Safari 6.0.5.

This problem has spilled over into the unresolved CLJS-890. This is a long thread, so see my recap comment, my failed efforts to reproduce the bug and my proposed final patch, which can be easily extended for the symbol case with a new "symbol" clause in the case statement.

Comment by Roman Scherer [ 21/Apr/16 4:20 PM ]

Thanks Francis, I'll take a look at this discussion.

Comment by Roman Scherer [ 21/Apr/16 5:12 PM ]

Since one can still make so called Symbol wrapper objects via the
Object() function [1], I added one more test case for this:

(js/Object (.for js/Symbol "x"))

Also the updated patch defines the test case only if js/Symbol exists?
in the JavaScript engine.

[1] https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol





[CLJS-1627] jsdoc parsing fails to recognize union types, breaking resolution Created: 18/Apr/16  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Patrick Killean Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: patch

Attachments: Text File CLJS-1627-4.patch    
Patch: Code and Test

 Description   

The Closure Spec For Union Types states that parentheses are necessary for union type expressions. Trying this ...

(defn foo
  "@param {(IBar|IMap)} x"
  [x] 
  ...)

Raises a Closure Error :

...ERROR - Bad type annotation. expected closing }
* @param {user.(IBar|user.IMap)}

This is because comp/resolve-types treats the parentheses as a part of the type tokens and incorrect var resolution occurs as a result. In addition, the compiler emits multiple resolved types separated by "|" characters but does not enclose them in parentheses to create a valid union type.



 Comments   
Comment by Patrick Killean [ 18/Apr/16 4:36 PM ]

This patch includes:

  • comp/resolve-types now removes parentheses when present and emits them when >1 type is detected. This makes parenthesis use optional and existing code remains unbroken (with the added benefit that it may work now)
  • changes to comp/resolve-type
    1. checks for js globals like document or window which are recognized by closure
    2. allows dot.delimited.forms to pass through so we can use types defined in externs and avoid unnecessary resolution
    3. uses ana/resolve-existing-var with a "unresolved jsdoc type" warning
    4. checks if a resolved var is a protocol and warns otherwise. This is more informative than Closure's standard unrecognized type error
  • a test for comp/resolve-types
Comment by David Nolen [ 21/Apr/16 12:45 PM ]

Thanks will try to look more closely at this tomorrow.

Comment by David Nolen [ 23/Apr/16 2:03 PM ]

The patch is getting there, please remove the `js-doc-type` meta stuff. Just extend the signature of resolve-existing-var to take an additional parameter - the confirm-var-exists handler.

Comment by Patrick Killean [ 09/May/16 4:58 PM ]

CLJS-1627-1.patch:
resolve-existing-var now has an additional arity that accepts a missing-var handler passed to confirm-existing-var

Comment by Patrick Killean [ 10/May/16 6:16 AM ]

This has revealed a problem where deftype + defrecord using Object protocols emit resolved names when really they shouldn't. For example : "@implements {cljs.core.async.impl.timers.Object}" --> Bad Type Annotation

Since Object is a special case simply excluding it from the comments should fix it. Another patch incoming

Comment by Patrick Killean [ 10/May/16 7:42 AM ]

CLJS-1627-2.patch:
The emit* methods for deftype and defrecord now filter out Object protocols.

This produced an interesting result! With no more bad type annotations, static analysis can now proceed... and it has alot to say. Theres all kinds of info now about arity discrepencies (particularly cljs.core.IndexedSeq), type mismatches, and more. It even includes a type coverage percentage. Lots to parse here but very cool.

Comment by Patrick Killean [ 18/May/16 4:26 PM ]

CLJS-1627-3.patch:

  • fix require extern
  • add type application support for Array & Object
  • GC likes uppercase for Object & Array, lowercase for string, number.
  • support for explicit nullable types, variable typed arg
  • function type context modifiers this + new

Missing is the GC 'record type' . It also may be useful to fill out the node externs for common types

Comment by Patrick Killean [ 20/May/16 11:42 AM ]

CLJS-1627-4.patch:

  • fix a few problems in last patch
  • add record type support. Everything here should be covered




[CLJS-1625] Clojurescript macros used in named function are expanded two times because the analyzer performs a two pass analysis when analyzing named functions Created: 16/Apr/16  Updated: 19/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Ewen Grosjean Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1625.patch    

 Comments   
Comment by Ewen Grosjean [ 16/Apr/16 9:41 AM ]

During the first analysis of named functions, only the function definition is analyzed in order to know its function-ness/arity. Its body is only analyzed during the second pass.

Comment by Kevin Downey [ 17/Apr/16 12:09 AM ]

http://dev.clojure.org/jira/browse/CLJS-1617 seems to add a similar issue





[CLJS-1623] using `with-redefs` to add meta to a function via `with-meta` fails when using advanced compilation Created: 12/Apr/16  Updated: 06/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Justin Cowperthwaite Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Given the following:

(ns test.cljs)

(enable-console-print!)

(defn print-foobar [] (println "foobar"))

(defn test-redefs-meta []
  (print-foobar)
  (with-redefs [print-foobar (with-meta print-foobar {:foo "bar"})]
    (print-foobar)))

(test-reefs-meta)

running with `:optimizations :none`, it correctly prints:

foobar
footer

However, running with `:optimizations :advanced`, it prints:

foobar
main.js:232 Uncaught TypeError: te is not a function(anonymous function) @ main.js:232

Reproduced on r1.8.40 and current master (29eb8e0).



 Comments   
Comment by Justin Cowperthwaite [ 14/Apr/16 5:42 PM ]

it seems that the actual issue is with having :static-fns true (as is default under :optimizations :advanced)

Comment by David Nolen [ 06/May/16 2:01 PM ]

This issue needs to provide some hypothesis of what is going wrong.





[CLJS-1622] `with-redefs` can cause `&` argument to be assigned incorrectly under advanced compilation Created: 12/Apr/16  Updated: 12/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Peter Jaros Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Given the following:

(ns with-redefs-bug.core)

(enable-console-print!)

(defn a-function [arg-1 arg-2 & args]
  nil)

(with-redefs [a-function (fn [& args] args)]
  (prn (a-function :arg-1))
  (prn (a-function :arg-1 :arg-2))
  (prn (a-function :arg-1 :arg-2 :arg-3)))

Under :optimizations :none, this code correctly prints:

(:arg-1)
(:arg-1 :arg-2)
(:arg-1 :arg-2 :arg-3)

However, under :optimizations :advanced, this code prints:

(:arg-1)
(:arg-1 :arg-2)
:arg-1

That is, as long as the function is called with exactly or less than the number of non-variadic arguments in the original function bound to a-function, args is (correctly) bound to a seq of all the arguments, but if any more arguments are given, args is bound to the first argument.

Also, under either compilation, the following warning is generated:

WARNING: Wrong number of args (1) passed to with-redefs-bug.core/a-function at line 9

That surprises me, but since it happens under both methods, perhaps it's intentional.



 Comments   
Comment by Peter Jaros [ 12/Apr/16 4:21 PM ]

Reproduced on r1.8.40 and current master (29eb8e0).

Comment by Mike Fikes [ 12/Apr/16 8:24 PM ]

This is actually not specific to :optimizations :advanced, but to :static-fns true.





[CLJS-1620] In JavaScript ES2015 modules default export name is munged to default$ Created: 08/Apr/16  Updated: 08/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Roman Liutikov Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

When using a foreign lib which is ES2015 module with default export, the value which is being exported is assigned to a property default on a namespace object. In ClojureScript code this means one should call to default var of that namespace. However in complied output of ClojureScript code the name default is getting munged to default$.

export default function inc(v) {
  return v + 1;
}
(ns cljs-example.core
  (:require [lib.inc :as lib]))

(lib/default 0)
goog.provide("module$lib$inc");
function inc$$module$lib$inc(v){return v+1}
module$lib$inc.default=inc$$module$lib$inc
// Compiled by ClojureScript 1.8.40 {}
goog.provide('cljs_example.core');
goog.require('cljs.core');
goog.require('module$lib$inc');
module$lib$inc.default$.call(null,(0));

//# sourceMappingURL=core.js.map


 Comments   
Comment by David Nolen [ 08/Apr/16 2:42 PM ]

One possible path around this is to respect the Closure Compiler language setting when munging instead of blindly munging ECMA-262 keywords. A patch that adopts this approach would be welcome.





[CLJS-1618] `extend-type string` doesnt work without wrapping the string object into `(str)` Created: 07/Apr/16  Updated: 10/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Yehonathan Sharvit Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

(ns my.car)

(defprotocol Car
(drive [this]))

(extend-type js/String
Car
(drive [this] (map #({"a" "A"} %) [this (str this)])))

(drive "a"); (nil "A") expected ("A" "A")

See the reproduction of the bug in a live environment with KLISPE here: http://app.klipse.tech/?sourcce=jira&cljs_in=(ns%20my.car)%0A%0A(defprotocol%20Car%0A%20%20(drive%20%5Bthis%5D))%0A%0A(extend-type%20js%2FString%0A%20%20Car%0A%20%20(drive%20%5Bthis%5D%20(map%20%23(%7B%22a%22%20%22A%22%7D%20%25)%20%5Bthis%20(str%20this)%5D)))%0A%0A%0A(drive%20%22a%22)%0A



 Comments   
Comment by Francis Avila [ 07/Apr/16 6:27 PM ]

This is because of boxing and the implementation of cljs.core._EQ_

By extending type js/String (the String object/class in javascript) instead of "string", your "this" will be the boxed string rather than the primitive string (in non-strict js mode--in strict mode it will be the primitive also). The (str this) is coercing the boxed string back to a primitive string.

The core issue is really:

(= (js/String. "a") "a") ;=> false
;; thus
({"a" "A"} (js/String. "a")) ;=> nil

You should really use

(extend-type string ...)
Comment by Francis Avila [ 07/Apr/16 6:41 PM ]

BTW this appears to be different from Clojure where primitives and boxed-primitives appear equal:

;; Clojure code
(= (String. "a") "a")
=> true
(= (Long. 1) 1)
=> true
(= (Long. 1) (long 1))
=> true

Not sure if clojurescript should try to replicate this more closely or not.

Clojurescript bottoms out with triple-equals in most cases, which is why primitives and boxes do not compare equal. To get them to compare equal would require adding special (instance? js/BOXED x) checks and some modifications to existing -equiv implementations which extend primitive types. (e.g. (extend-type number IEquiv ...) uses identical? without checking if the right-hand side is boxed or not.)

Comment by Mike Fikes [ 10/Apr/16 12:24 AM ]

As Francis alludes to, this is not a bug. If you do (doc extend-type), it indicates the type-sym can be string to cover the base type js/String, and it elaborates with

Note that, for example, string should be used instead of js/String.
and if a user does try to use js/String as the type-sym argument, a diagnostic is issued:

WARNING: Extending an existing JavaScript type - use a different symbol name instead of js/String e.g string at line 1




[CLJS-1616] Self-host: improve documentation for compile-str Created: 06/Apr/16  Updated: 06/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Yehonathan Sharvit Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

It's not clear at all how to use the `opts` arguments for compile-str.

In the code - https://github.com/clojure/clojurescript/blob/c3899acf797eb6779c53b313f5606c5018360b83/src/main/cljs/cljs/js.cljs#L660 - we
only have
:load - library resolution function, see load-fn
:source-map - set to true to generate inline source map information

In fact, there is also :verbose and ::def-emits-var

They are not documented.

Are there more options?






[CLJS-1615] Inlining truth checks can lead to better optimisation results Created: 04/Apr/16  Updated: 08/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Antonin Hildebrand Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1615.patch    
Patch: Code and Test

 Description   

I had a situation when DCE was (naively) expected but didn't happen (in :advanced compilation mode). I did some exploration and discovered that inlined truth check code is better understood by Closure Compiler and leads to expected optimisation (for some reasons).

I believe understanding this behaviour and exploiting it where desirable could lead to more predictable code generation without resorting to using cljs type hints.



 Comments   
Comment by Antonin Hildebrand [ 04/Apr/16 2:54 PM ]

The backstory as posted to #cljs-dev in Slack:

When @domkm requested proper dead code elimination in cljs-devtools, I got burnt by the need to explicitly specify `(if ^boolean js/goog.DEBUG …)` hint to get `:closure-defines` working under :advanced build[1]. It was unexpected to me that closure compiler cannot see that optimization and does not inline truth test for js/goog.DEBUG “constant”. So I started poking around and found a way how to aggressively inline checked truth checks in a compatible way (I believe). I also believe this could potentially open optimizations in other places. I think we should explore `@nosideeffects` annotation[2] and tag core functions where appropriate.

[1] https://github.com/binaryage/cljs-devtools/releases/tag/v0.5.3
[2] https://developers.google.com/closure/compiler/docs/js-for-compiler?hl=en#tag-nosideeffects

Comment by Francis Avila [ 05/Apr/16 3:11 PM ]

`@nosideeffects` should only be relevant for extern files where the compiler cannot see the implementation and know if the function is pure. Normally the compiler just analyzes the function to see if it side-effects.

This may be a performance win as well: it looks like advanced compile will unwrap the function expression entirely in some cases (both expression and statement contexts), so no more megamorphic calls to truth_ or even function object allocations.

However, I don't think there's a guarantee that the closure compiler will always understand enough to remove the need for the ^boolean hint for defines in all cases.





[CLJS-1610] Refs api Created: 26/Mar/16  Updated: 28/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: James Laver Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

It would be nice to support the refs API in clojurescript to ease porting of libraries which require it. Obviously usage of it would not bring concurrency benefits, but it would at least allow some existing clojure code to run.

I've satisfied myself it can be done reasonably easily (see https://github.com/jjl/clojurescript/tree/ref-support ), and I can't really see any downsides.

For the minute, this has been put into the cljs.stm namespace. I don't know whether it should be a feature you are automatically opted into, so I didn't put it into core. Thoughts?



 Comments   
Comment by David Nolen [ 28/Mar/16 6:44 AM ]

It's definitely worth considering. If you've done the work, attach an actual patch for review. In general we try to avoid links outside.





[CLJS-1607] Advanced compilation bug with `specify!` in JS prototypes Created: 23/Mar/16  Updated: 23/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: António Nuno Monteiro Assignee: David Nolen
Resolution: Unresolved Votes: 1
Labels: None
Environment:

affects 1.8.34



 Description   

compiling this code with advanced optimizations

(ns bug.core)

(defprotocol IBug
  (bug [this other] "A sample protocol"))

(defn MyBug [])
(specify! (.-prototype MyBug)
  IBug
  (bug [this other]
    "bug")
  Object
  (foo [this]
    (bug this 3))) ;; line 13

causes the following warning:

WARNING: Use of undeclared Var bug.core/x14072 at line 13


 Comments   
Comment by António Nuno Monteiro [ 23/Mar/16 1:42 PM ]

narrowed it down to this line (https://github.com/clojure/clojurescript/blob/f0ac4c92006ac618516c11e9ca3527904d35d4af/src/main/clojure/cljs/compiler.cljc#L936) being called in `:advanced` because it passes the check of cljs-static-fns in that case





[CLJS-1604] Self-host: cljs.js/compile-str causes a javascript error Created: 19/Mar/16  Updated: 14/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Yehonathan Sharvit Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bootstrap, bug


 Description   

When requiring `cljs.js` and calling `cljs.js/compile-str` with `:optimizations :advanced`
I get the following error in the browser:
"Uncaught TypeError: Cannot set property 'rd' of undefined"

Steps to reproduce:

1. Make a directory
2. Copy shipping cljs.jar into the directory
3. Make an index.html, src/hello_world/core.cljs, and build.clj file with contents as below.
4. java -cp cljs.jar:src clojure.main build.clj
5. Open index.html with Chrome and view the JavaScriptConsole in Chrome.

index.html:

<html>
<body>
<script type="text/javascript" src="out/main.js"></script>
</body>
</html>
src/hello_world/core.cljs:
(ns hello-world.core
(:require [cljs.js :as cljs]))

(set! (.. js/window -cljs -user) #js {})

(cljs/compile-str (cljs/empty-state) "" indentity)

build.clj:

(require 'cljs.build.api)

(cljs.build.api/build "src"
{:output-to "out/main.js"
:optimizations :whitespace})

(System/exit 0)



 Comments   
Comment by Yehonathan Sharvit [ 19/Mar/16 5:31 PM ]

I need to fix the title of the issue: "Self-host: in advanced compilation - cljs.js/compile-str causes a javascript error"

Comment by Mike Fikes [ 30/Mar/16 11:14 PM ]

You can only use up to :optimizations :simple with self-host. See https://github.com/clojure/clojurescript/wiki/Optional-Self-hosting#production-builds

Discussion: One rationale for this is that the emitted code, in order to be executable, needs access to non-Closure-munged/DCEd symbols from the standard ClojureScript lib. Perhaps this limitation need only exist for eval-str, (while not for compile-str, analyze-str, etc.)

Comment by Mike Fikes [ 14/Apr/16 7:02 AM ]

I'd recommend closing this as declined (no plans exist to support self-host with :advanced).





[CLJS-1601] Optimize cljs.core dump Created: 12/Mar/16  Updated: 01/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Nikita Beloglazov Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: bootstrap

Attachments: Text File CLJS-1601.patch     Text File CLJS-1601.patch    

 Description   

When building cljs source that uses cljs.js namespace the final js file is quite huge: 6.4M. As described in wiki: https://github.com/clojure/clojurescript/wiki/Optional-Self-hosting it mostly consists of analysis cache of the cljs.core namespace. As a workaround, the wiki article suggests dumping cache to a separate file and load it at runtime instead of bundling in js binary. I think it is possible to have something in between that doesn't require additional efforts from a user and also optimizes the size of the js file. The idea that instead of dumping cache as raw clojure data-structure it is serialized to string. This way compiler won't compile cache into js (which adds a lot of code) and leave it a string. At runtime, this string will be parsed back to clojure using tools.reader.

Here is the proposal: https://gist.github.com/nbeloglazov/0bf163fb62fa4b61d446

Checking locally it reduces the size of js file from 6.4M to 2.7M which I think quite good. The downside is that now js has to do more work on runtime (parse huge string) when today it simply read js code and evaluates it. But I don't think if it's a big concern. If it is desired to keep all behavior a new option can be added for :dump-core compiler setting, something like :dump-core :string that enables string serialization of the cache.

Does it sound reasonable?



 Comments   
Comment by Nikita Beloglazov [ 27/Mar/16 8:54 PM ]

Attaching suggested fix. Analysis cache is serialized to string and read back to clojure datastructure when cljs.js is initialized.

Comment by David Nolen [ 28/Mar/16 6:39 AM ]

Please change the patch so this optional as you've suggested.

Comment by David Nolen [ 28/Mar/16 6:40 AM ]

Also have you submitted your Clojure CA yet?

Comment by Nikita Beloglazov [ 28/Mar/16 1:35 PM ]

Will do. Yes, I've submitted CA. I used my official name, Mikita Belahlazau there.

Comment by Nikita Beloglazov [ 29/Mar/16 12:16 AM ]

Updated patch that adds option to serialize core analysis cache as string. Possible values of :dump-core are :raw, :string, :none. Old true/false values supported for backward compatibility.

As for default, current patch uses :raw, but I think it makes more sense to use :string. Saving extra few mb of final js is quite good. I think most devs won't go deep into figuring out why js is big and just leave it as it is. Additional one-time parsing performance hit :string introduces acceptable: when :string is used, page loads in 1s while with :raw the time is ~800ms.





[CLJS-1600] Destructuring defprotocol fn args causes defrecord impls to silently fail Created: 11/Mar/16  Updated: 11/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: James Henderson Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   
(defprotocol IFoo
  (foo-fn [_ {:keys [a b] :as args}]))

(defrecord Foo []
  IFoo
  (foo-fn [_ {:keys [a b] :as args}]
    args))

(foo-fn (->Foo) {:a 1, :b 2})

returns

{:keys [1 2], :as {:a 1, :b 2}}

in CLJS, rather than

{:a 1, :b 2}

as Clojure does.

Redefining IFoo to

(defprotocol IFoo
  (foo-fn [_ args]))

causes the issue to go away.

While it's quite a minor bug, it was quite a hard one to track down, in practice - I didn't think to look at the protocol definition when the record was breaking, even after having used ClojureScript for a good few years!

Cheers,

James






[CLJS-1599] UUIDs are not equal for upper/lower case strings Created: 07/Mar/16  Updated: 11/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Nikolay Durygin Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Windows 7x64



 Description   

UUIDs generated for strings in different case (one is upper and one is lower) are equal.

For example (= (uuid "071C600F-B72B-44AE-8A15-9366EA1BB9D9") (uuid "071c600f-b72b-44ae-8a15-9366ea1bb9d9")) returns false.

Spec http://www.itu.int/rec/T-REC-X.667/en says following:

6.5.4 Software generating the hexadecimal representation of a UUID shall not use upper case letters.
NOTE - It is recommended that the hexadecimal representation used in all human-readable formats be restricted to lower-case
letters. Software processing this representation is, however, required to accept both upper and lower case letters as specified
in 6.5.2.



 Comments   
Comment by Gary Fredericks [ 07/Mar/16 8:17 AM ]

Would this be a good time to change the internal representation from a string to either a pair of goog.math.Longs or a quartet of "32-bit" integer doubles?

Comment by Nikolay Durygin [ 09/Mar/16 2:22 AM ]

Is there any special need? Issue described above can be solved by lower casing all strings inside uuid. Another problem - the fact that uuid doesn't complain if non uuid format string is passed can be solved with regex.





[CLJS-1598] Honor printing of function values via IPrintWithWriter Created: 03/Mar/16  Updated: 08/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1598.patch    
Patch: Code

 Description   

If a user wishes to define how function values are printed, allow that to be controlled via IPrintWithWriter with code like

(extend-type function
  IPrintWithWriter
  (-pr-writer [obj writer opts]
    ,,,))


 Comments   
Comment by Mike Fikes [ 03/Mar/16 10:28 AM ]

Can be tested manually:

$ script/nashornrepljs 
To quit, type: :cljs/quit
cljs.user=> inc
#object[cljs$core$inc "function cljs$core$inc(x){
return (x + (1));
}"]
cljs.user=> (extend-type function
  IPrintWithWriter
  (-pr-writer [obj writer _]
    (let [name (.-name obj)
          name (if (empty? name)
                 "Function"
                 name)]
      (write-all writer "#object[" name "]"))))
#object[Function]
cljs.user=> inc
#object[cljs$core$inc]
Comment by David Nolen [ 11/Mar/16 1:04 PM ]

The problem is this makes printing slower. For people using EDN as interchange format this may be a problem. Would need to see some numbers.

Comment by Antonin Hildebrand [ 08/Apr/16 2:11 PM ]

I'm not sure what is the difference between implements? and satisfies?. But by reading the code I would assume that it should be printed by this line:
https://github.com/clojure/clojurescript/blob/9a2be8bc665385be1ef866e2fd76b476c417d2bf/src/main/cljs/cljs/core.cljs#L9056-L9057

Don't we want to change implements? to satisfies? there? Not sure about (perf) implications.





[CLJS-1591] Compilation time go up significantly when nesting multimethods Created: 25/Feb/16  Updated: 14/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Marian Schubert Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: cljs, compiler

Attachments: Text File CLJS-1591.patch    

 Description   

Code like this takes 140 seconds to compile on my machine. Regular functions don't seem to trigger this behaviour.

(ns slow.core)

(defmulti my-multimethod (fn [x] :whatever))

(defn this-is-sloow-to-compile []
  (my-multimethod
   (my-multimethod
    (my-multimethod
     (my-multimethod
      (my-multimethod
       (my-multimethod
        (my-multimethod
         (my-multimethod
          (my-multimethod
           (my-multimethod
            (my-multimethod
             (my-multimethod
              (my-multimethod
               (my-multimethod
                (my-multimethod
                 (my-multimethod
                  (my-multimethod
                   (my-multimethod
                    (my-multimethod
                     (my-multimethod {})))))))))))))))))))))

$ rm -rf target/ && ./scripts/release 
Building ...
Analyzing jar:file:/Users/maio/.m2/repository/org/clojure/clojurescript/1.7.228/clojurescript-1.7.228.jar!/cljs/core.cljs
Compiling src/slow/core.cljs        <-- here it spends most of the time
Applying optimizations :advanced to 11 sources
... done. Elapsed 141.85386191 seconds

Whole project is here https://github.com/maio/slow-cljs-build



 Comments   
Comment by Mike Fikes [ 04/Mar/16 4:32 PM ]

Hrm. This fix is evidently in the Closure compiler used by 1.7.228: https://github.com/google/closure-compiler/issues/1049

Comment by Thomas Heller [ 06/Mar/16 6:25 AM ]

@mfikes the slowdown is not related to the Closure Compiler since it happens when compiling cljs->js not when optimizing.

The reason for the slowdown is due to the arguments of a multimethod call being analyzed twice (or more in case of deep nesting).

See [1] for the problematic code.

multimethods are not fn-var? so the or does not short circuit and (all-values? argexprs) is reached. This forces the argexprs lazy-seq (thereby analyzing the args). Since the args are not all-values? the else-branch of the if is taken, which then later causes the args to be analyzed again. My math is weak but I'm not mistaken this is O(n!), explaining the dramatic slowdown.

Every var that is not fn-var? is affected by this:

(defn test [& args] :whatever)
(def my-multimethod test)
;; or
(def my-multimethod
  (reify
    IFn
    (-invoke [a] :whatever)))

One solution would be to fix all-values? that instead of running through analyze it could just check whether all args are fixed literals (ie. not list? but all of number? string? symbol? keyword? etc.).

I'm not really sure why the else-branch in [1] exists at all but I assume it is to work around some JS quirks. I will hold off on writing a patch until I figure out why the extra let introduced in the else-branch is needed.

[1] https://github.com/clojure/clojurescript/blob/f58dcdf4dc37ef52d4eb1e2b7c994282bf6351f5/src/main/clojure/cljs/analyzer.cljc#L2257-L2263

PS: forgot to add that this does not happen with :static-fns false since it also prevents the else from being reached.

Comment by Thomas Heller [ 06/Mar/16 6:55 AM ]

The else was introduced in CLJS-855 and is sort of required for invokes without arity information and :static-fns true.

Changing all-values? to just check literals instead of analyzing should be a valid solution.

Comment by Thomas Heller [ 14/Mar/16 8:31 AM ]

The patch removes the extra analyze and instead just checks the few cases that can actually be used without assignment first.

This removes the slowdown while keeping all the functionality.





[CLJS-1587] Duplicate keys via quoting Created: 24/Feb/16  Updated: 15/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: File CLJS-1587.diff    
Patch: Code and Test

 Description   

For

#{1 '1}

you get

#{1 1}


 Comments   
Comment by Peter Schuck [ 03/Mar/16 10:01 PM ]

This happens for the has-set macro and the hash-set literal. Here's what I get from the repl

cljs.user=> (hash-set 1 '1 2 '2 3 '3 4 '4 5)
#{1 2 3 4 5}
cljs.user=> (hash-set 1 '1 2 '2 3 '3 4 '4)
#{1 1 2 2 3 3 4 4}
cljs.user=> #{ 1 '1 2 '2 3 '3 4 '4}
#{2 1 4 4 3 2 1 3}
cljs.user=> #{ 1 '1 2 '2 3 '3 4 '4 5}
#{2 1 4 4 3 2 5 1 3}
cljs.user=> #{ 1 '1 2 '2 3 '3 4 '4 5 '5}
#{2 5 1 4 4 3 2 5 1 3}
cljs.user=> (apply hash-set [1 '1 2 '2 3 '3 4 '4])
#{1 2 3 4}

Calling hash-set as a function gives the correct results. The hash-set macro gives the incorrect results until we have more then 8 elements and uses the fromArray method on PersistentHashSet to build the set instead of creating a literal PersistentArrayMap for the set. The literal notation is incorrect no matter how many elements there are.

Comment by Rohit Aggarwal [ 15/Jun/16 8:02 AM ]

The underlying problem for both is the same in that a PersistentHashSet is being created directly using a PersistentArrayMap where the keys are the elements from the provided sequence. It manifests itself in two places though.

Comment by Rohit Aggarwal [ 15/Jun/16 9:32 AM ]

I've taken the approach that if we see a quoted constant then don't create the PersistentHashSet directly and instead go via the fromArray function.

Patch has the fix and a test.





[CLJS-1576] cljs.js sourcemap support throws on non-latin1 characters Created: 17/Feb/16  Updated: 18/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Matt Huebert Assignee: Matt Huebert
Resolution: Unresolved Votes: 0
Labels: bootstrap

Attachments: Text File CLJS-1576.patch    
Patch: Code

 Description   

In cljs.js/append-source-map we encode the source-map string in base64 without escaping non-latin1 characters. In Chrome, this throws the error: "DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."

Source: https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/js.cljs#L152

The problem & a couple of solutions are explained here: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem



 Comments   
Comment by David Nolen [ 18/Feb/16 8:21 AM ]

Can bootstrapped users apply this and verify it works for them? Thanks.

Comment by Mike Fikes [ 18/Feb/16 10:03 AM ]

I tried this with Planck and I can confirmed that, with a function name in Japanese, sourceMappingURL does indeed change and then includes base-64 data that covers my entire set of functions (whereas previously it did not), but the Japanese function name appears to have been munged into some Latin-1 characters (which I suppose is the point of the patch).

With Planck, I can't confirm the overall functionality as Planck doesn't make use of this information with JavaScriptCore (it instead uses equivalent info stored in map files).

So, as far as I can tell, this patch is good in that it appears to be doing the right thing when run with the bootstrap compiler.

Comment by David Nolen [ 18/Mar/16 1:45 PM ]

OK, patch looks ok to me but it needs to be rebased to master.





[CLJS-1575] Combination of - and _ params causes JSC_DUPLICATE_PARAM Created: 17/Feb/16  Updated: 18/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Peter Jaros Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

The following function definitions each cause ERROR: JSC_DUPLICATE_PARAM. Parse error. Duplicate parameter name "_" at compile time with :advanced compilation:

Unable to find source-code formatter for language: clojure. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
(defn causes-duplicate-param [{_ :foo}]
  (reify
    Object
    (a-function [-]
      (fn [] "arbitrary function inside a-function body"))))

(defn causes-duplicate-param [{- :foo}]
  (reify
    Object
    (a-function [_]
      (fn [] "arbitrary function inside a-function body"))))

They generate, respectively:

duplicate_param_name_demo.core.t_duplicate_param_name_demo$core1601.prototype.a_function = ((function (map__1599,map__1599__$1,_){
return (function (){
var self__ = this;
var _ = this;
return ((function (_,map__1599,map__1599__$1,_){
return (function (){
return "arbitrary function inside a-function body";
});
;})(_,map__1599,map__1599__$1,_))
});})(map__1599,map__1599__$1,_))
;

duplicate_param_name_demo.core.t_duplicate_param_name_demo$core1612.prototype.a_function = ((function (map__1610,map__1610__$1,_){
return (function (){
var self__ = this;
var _ = this;
return ((function (_,map__1610,map__1610__$1,_){
return (function (){
return "arbitrary function inside a-function body";
});
;})(_,map__1610,map__1610__$1,_))
});})(map__1610,map__1610__$1,_))
;

(Notice the duplicate _ param on the 5th line of each.)

The following do not:

Unable to find source-code formatter for language: clojure. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
(defn causes-duplicate-param [{- :foo}]
  (reify
    Object
    (a-function [-]
      (fn [] "arbitrary function inside a-function body"))))

(defn causes-duplicate-param [{_ :foo}]
  (reify
    Object
    (a-function [_]
      (fn [] "arbitrary function inside a-function body"))))

They generate:

duplicate_param_name_demo.core.t_duplicate_param_name_demo$core1601.prototype.a_function = ((function (map__1599,map__1599__$1,_){
return (function (){
var self__ = this;
var ___$1 = this;
return ((function (___$1,map__1599,map__1599__$1,_){
return (function (){
return "arbitrary function inside a-function body";
});
;})(___$1,map__1599,map__1599__$1,_))
});})(map__1599,map__1599__$1,_))
;

duplicate_param_name_demo.core.t_duplicate_param_name_demo$core1612.prototype.a_function = ((function (map__1610,map__1610__$1,_){
return (function (){
var self__ = this;
var ___$1 = this;
return ((function (___$1,map__1610,map__1610__$1,_){
return (function (){
return "arbitrary function inside a-function body";
});
;})(___$1,map__1610,map__1610__$1,_))
});})(map__1610,map__1610__$1,_))
;

(Notice that one of the {} params has become __$1.)

My guess, though I haven't looked into the compiler code, is that the compiler escapes {} to _$1 when it would conflict with another {}, and also it translates - to {}, but it doesn't notice the conflict when the _ClojureScript symbols are different.



 Comments   
Comment by Peter Jaros [ 17/Feb/16 11:44 AM ]

Forgive the weird formatting errors. I couldn't find a preview function and there doesn't appear to be a way to edit the issue now that it's posted.

Comment by David Nolen [ 18/Mar/16 1:46 PM ]

This is because of munging they will become the same thing. Patch welcome.





[CLJS-1574] CLJS string equivalence is very slow in Chrome Created: 16/Feb/16  Updated: 22/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Stephen Nelson Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: performance
Environment:

Google Chrome 48.0.2564.109 on Mac OS X 10.11.3
Safari 9.0.3 (11601.4.4) on Mac OS X 10.11.3



 Description   

Clojurescript's equivalence for strings in Google Chrome is ~1000 times slower than equivalent javascript functionality, and ~1000 times slower than the same function in Safari.

Google Chrome
js equiv:  0.005 seconds
cljs equiv:  1.898 seconds
Safari
js equiv:  0.005 seconds
cljs equiv:  0.006 seconds
(def size (* 128 1024))

(defn now []
  (.getTime (js/Date.)))

(defn delta [b a]
  (str (/ (- b a) 1000) " seconds"))

(deftest test-js-eq-perf
  (let [str1  (apply str (repeat size "a"))
        str2  (apply str (repeat size "a"))
        start (now)
        _     (is (js* "~{} == ~{}" str1 str2)
                  "js equivalence")
        end   (now)
        ]
    (println "js equiv: " (delta end start))))

(deftest test-cljs-eq-perf
  (let [str1  (apply str (repeat size "a"))
        str2  (apply str (repeat size "a"))
        start (now)
        _     (is (= str1 str2)
                  "cljs equivalence")
        end   (now)
        ]
    (println "cljs equiv: " (delta end start))))


 Comments   
Comment by Stephen Nelson [ 16/Feb/16 6:06 PM ]

This bug only occurs when cljs.pprint has been required.

Comment by Stephen Nelson [ 16/Feb/16 6:38 PM ]

After a whole lot of binary search, here's a minimal reproduction. When cljs.pprint is loaded it constructs write-option-table. It seems that constructing a hash map with the keys :added and :ns causes a call to (= :added :ns), which is sufficient to cause string equality to become extremely slow.

(ns hello-world.core)

(enable-console-print!)

(def size (* 128 1024))

(defn now []
  (.getTime (js/Date.)))

(defn delta [b a]
  (str (/ (- b a) 1000) " seconds"))

(defn test [] 
  (let [str1  (apply str (repeat size "a"))
        str2  (apply str (repeat size "a"))
        start (now)
        _     (= str1 str2)
        end   (now)
        ]
      (println "cljs equiv: " (delta end start))))

(test)

(= :added :ns)

(test)
Comment by Peter Schuck [ 17/Feb/16 4:50 PM ]

Is the ClojureScript compiled with options :optimizations :advanced or :static-fns true? Compiling ClojureScript without those options results in call indirection for all function calls which might explain the slowdown. See http://swannodette.github.io/2015/03/16/optimizing-clojurescript-function-invocation/ for more information.

Comment by Stephen Nelson [ 17/Feb/16 9:06 PM ]

This happens with :advanced, :simple, and without optimisations. Stepping through the generated javascript seems to indicated that the slow down comes from the VM's internal function dispatch. Regardless, I don't think that the extra function calls related to dynamic dispatch in clojurescript could add minutes of overhead per call. Note that the test case above only uses 128k of string data, the case where I encountered this issue first used ~512K and took about 5 minutes to complete a single function call.

Comment by Francis Avila [ 17/Feb/16 9:14 PM ]

I have reproduced this in Chrome for Linux, on :none and :advanced optimization levels using different test code. I verified the result of the compare so the JIT won't optimize it away and I used performance.mark() and performance.measure() for timing, although none of this should have mattered.

Every subsequent string compare after the first -equiv-invoking use of equal is significantly slower for no reason I can see. There are no intermediate GCs or anything to suggest that it should be slower--it just takes longer! The only thing I can think of is maybe the keyword-equals triggers a deopt because it makes the equal-function megamorphic, but the code is run so few times that there should not be jit optimizations kicking it at all. Also, the keyword-compare itself remains fast.

I suspect a Chrome/v8 bug. Possibly a different internal string representation kicks in for some reason which has a slower compare? This is only an issue for largish, non-constant strings, and the slowdown is proportional to string size. I'm going to try and reproduce this with pure JS.

Comment by Francis Avila [ 18/Feb/16 12:33 AM ]

All you need to reproduce this is to use the strict equality operator in a function body non-monomorphically. Subsequent executions of the function with strings (at least) which have not been compared before the polymorphic call will be very slow.

If you replace strict equality (triple-equal) with normal equality (double-equal), this issue goes away.

This is clearly a Chrome/v8 bug, but I'm not sure where to report it.

Minimal pure-javascript reproduction:

function str(size) {
  var s = "";
  for (var i = 0; i < size; i++) s += "a";
  return s;
}

function eq(x, y) {
  performance.mark("start");
  x === y; // No slowdown if use == instead
  performance.mark("end");
}

function print_measures() {
  performance.getEntriesByType("measure")
  .forEach(entry => console.log(entry.name, entry.duration));
}

var s1 = str(64 * 1024);
var s2 = str(64 * 1024);
var s3 = str(64 * 1024);

eq(s1, s2);
performance.measure("eq(s1, s2)", "start", "end");

eq(0, 0);
performance.measure("eq(0, 0)", "start", "end");

eq(s1, s3);
performance.measure("eq(s1, s3)", "start", "end");

eq(s1, s2);
performance.measure("eq(s1, s2)", "start", "end");

eq(s1, s3);
performance.measure("eq(s1, s3)", "start", "end");

print_measures();

Results with Chrome 48.0.2564.109 (64-bit) on a slow iMac with OS X 10.11.3

eq(s1, s2)   4.465000000000003     // fast string compare
eq(0, 0)     0.009999999999990905  // break monomorphism of eq()
eq(s1, s3) 259.665                 // Now string compare is slow
eq(s1, s2)   0.019999999999924967  // Repeated call still fast
eq(s1, s3) 232.52499999999998      // ... but not from after the polymorphic invoke
Comment by Francis Avila [ 22/Feb/16 3:14 AM ]

Issue added to v8: https://bugs.chromium.org/p/v8/issues/detail?id=4773





[CLJS-1572] REPL doesn't give error for expressions with too many right parentheses. Created: 15/Feb/16  Updated: 24/May/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: J David Eisenberg Assignee: David Nolen
Resolution: Unresolved Votes: 2
Labels: repl
Environment:

Fedora 23, java version "1.8.0_40", javac 1.8.0_40, clojure 1.7.0


Attachments: Text File CLJS-1572.patch    
Patch: Code

 Description   

I was expecting an error message from this; using [org.clojure/clojurescript "1.7.228"]; the Clojure REPL does produce an error.

To quit, type: :cljs/quit
cljs.user=> (+ 3 5)))))
8


 Comments   
Comment by Mike Fikes [ 16/Feb/16 12:49 PM ]

A suggestion on a strategy to fix this: Make the ClojureScript REPL sequentially process all of the forms it can read on a line, just like the Clojure REPL does:

user=> 3 (+ 3 5) 7
3
8
7

If this is done, then the fix for this ticket will fall out “for free” and the ClojureScript REPL will error when it hits a form that appears to start with ).

Comment by Mike Fikes [ 21/Feb/16 4:01 PM ]

The REPL code is very close to working the way mentioned in the previous comment. It currently does not only because this line

https://github.com/clojure/clojurescript/blob/c59e957f6230c07e7a228070dd8eb393d5b8ce40/src/main/clojure/cljs/repl.cljc#L100

invokes code that causes a new PushbackReader to wrap things (discarding things):

https://github.com/clojure/clojurescript/blob/c59e957f6230c07e7a228070dd8eb393d5b8ce40/src/main/clojure/cljs/repl.cljc#L773-L775

If you either let the PushbackReader once and let that reader fn close over it, or otherwise comment out things so that a new PushbackReader is not created for each loop / recur, you will see that the code behaves as suggested in the previous comment, having the desired effect.

The only thing I see that would need to be additionally sorted out with such a patch is being a little more clever about when need-prompt evaluates to true, etc. (otherwise polishing thing so there are no missed corner cases).

Comment by Mike Fikes [ 21/Feb/16 11:02 PM ]

Attached a patch that, in essence makes the ClojureScript REPL behave like the Clojure REPL with respect to multiple items on a line and with respect to detecting malformed input. The patch is fairly straightforward, but could use some testing. I've tried things like

cljs.user=> 3_    ; where _ here is a space

cljs.user=> 3 4 5

cljs.user=> 3)

cljs.user=> 3))

cljs.user=> 3 [4
5]

cljs.user=> (let [x 1]
(+ 1 "a"))         ;; testing to make sure line numbers are right

All the above is looking good to me.

Here is the commit comment:

Take extra care to preserve the state of in so that anything beyond
the first form remains for reading. This fundamentally makes the
ClojureScript REPL behave like the Clojure REPL. In particular, it
allows entering multiple forms on a single line (which will be evaluated
serially). It also means that if malformed input lies beyond the initial
form, it will be read and will cause an exception (just like in the
Clojure REPL).

The bulk of the complexity in this commit has to do with the case where
a new line-numbering reader is established, so that errors in forms
can be associated with line numbers, starting with line 1 being the
first line of the form. This requires a little extra handling because
the source-logging-push-back-reader introduces an extra 1-character
buffer which must be transferred back to the original (pre-bound) in,
otherwise things like an unmatched extra paren right after a well-formed
form won't be detected (as the paren would be in the 1-char buffer and
discarded.)

Also, a Java PushbackReader needs to be eliminated, as it causes things
to fail to behave like the Clojure REPL.

Comment by Mike Fikes [ 21/Feb/16 11:14 PM ]

Note that one extremely useful thing this patch enables is pasting of multiple forms into a ClojureScript REPL!

This fails if pasted using the current cljs.jar, but works with the patch applied:

(def a 1)

(def b 2)

(def c (+ a b))

c
Comment by Mike Fikes [ 24/May/16 3:19 PM ]

I tested this with Figwheel [figwheel-sidecar "0.5.0-6"] and it worked properly evaluating multiple forms on a single line, evaluating pasted forms (as in the previous comment), and it properly indicates Unmatched delimiter ) for the case in the description.





[CLJS-1563] :source-map option to cljs.build.api/build should take nil Created: 07/Feb/16  Updated: 12/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Isaac Cambron Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

It should be possible to specify nil or false when providing the :source-map option to cljs.build.api/build, for example, like this:

(build {...
        :optimizations :whitespace
        :source-map (when debug? "somepath.js.map")})

Currently that causes:

Exception in thread "main" java.lang.AssertionError: Assert failed: :source-map nil must specify a file in the same directory as :output-to "target/js/zs-background.js" if optimization setting applied
(or (nil? (:output-to opts)) (:modules opts) (string? source-map)), compiling:(/Users/isaac/code/zensight/client/cljs/build.clj:66:1)

Using false has the same behavior. The alternative of conditionally assoc ing the key in works just fine, but is a tad awkward. It seems reasonably straightforward to fix - need to change that assert to check the value in the map and double-check that it's checked properly downstream. Happy to submit a patch if you'll take it.



 Comments   
Comment by Isaac Cambron [ 07/Feb/16 10:18 AM ]

Apologies for the formatting; forgot that backtick stuff doesn't work in Jira.

Comment by Mike Fikes [ 08/Feb/16 5:05 PM ]

Reformatted description.

Comment by David Nolen [ 12/Feb/16 2:36 PM ]

Patch welcome.





[CLJS-1562] WARN on hinted fn call type mismatch Created: 06/Feb/16  Updated: 18/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

If a call is made to a function that has hinted arguments (especially {^boolean} and {^number}), with an expression that is known to not be of that type, emit a diagnostic type mismatch warning.

An example that should emit a warning is:

(defn f [^boolean b])
(f 0)





[CLJS-1561] WARN if recur passes non-inferred type Created: 06/Feb/16  Updated: 23/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1561.patch    
Patch: Code

 Description   

Take this code as an example:

(defn f [^boolean b]
  (loop [x b]
    (if x
      (recur 0)
      :done)))

The type of x is inferred to be Boolean, but there is a recur form that can be statically deduced to be passing a non-Boolean.

This ticket asks that a WARN be issued for this case, and perhaps others (where maybe x itself is directly type hinted).



 Comments   
Comment by Mike Fikes [ 06/Feb/16 2:59 PM ]

Attached a patch which warns on for the case of boolean and number, since those two types have special handling.

Some example usage:

cljs.user=> (defn f [^boolean b]
       #_=>   (loop [x b]
       #_=>     (if x
       #_=>       (recur 0)
       #_=>       :done)))
WARNING: recur target parameter x has inferred type boolean, but being passed type number at line 4 
#'cljs.user/f
cljs.user=> (loop [x 1 y true z :hi]
       #_=>   (when false (recur 'a "hi" nil)))
WARNING: recur target parameter x has inferred type number, but being passed type cljs.core/Symbol at line 2 
WARNING: recur target parameter y has inferred type boolean, but being passed type string at line 2 
nil
cljs.user=> (loop [x 1 y true]
       #_=>  (when false (recur nil nil)))
WARNING: recur target parameter x has inferred type number, but being passed type clj-nil at line 2 
WARNING: recur target parameter y has inferred type boolean, but being passed type clj-nil at line 2 
nil
cljs.user=> (loop [x 1]
       #_=>   (let [y (inc x)]
       #_=>     (when false (recur (inc y)))))
nil
cljs.user=> (loop [b true]
       #_=>   (when false (recur (inc 1))))
WARNING: recur target parameter b has inferred type boolean, but being passed type number at line 2 
cljs.user=> (loop [x 1] 
       #_=>   (inc x) 
       #_=>     (when false (recur :hi)))
WARNING: recur target parameter x has inferred type number, but being passed type cljs.core/Keyword at line 3 
nil
cljs.user=> (loop [x :hello] 
       #_=>   (inc x) 
       #_=>     (when false (recur :hi)))
WARNING: cljs.core$macros/+, all arguments must be numbers, got [cljs.core/Keyword number] instead. at line 2 
nil




[CLJS-1559] Closure :libs ignored Created: 05/Feb/16  Updated: 05/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Dominykas Mostauskis Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

:libs compilation option doesn't work. Whether specifying directories, or specific files. If trying to `import` one of the js classes (properly namespaced with `goog.provide`) into clojurescript, compilation fails with "no such namespace". If the libs code is not referenced in clojurescript, it compiles, and the output directory does not contain the libs js files.

Compilation options:

(cljs.closure/build
    "src/main/clojurescript"
    {:main 'example.core
     :libs ["/src/main/javascript/"]
     :optimizations :none
     :output-dir "js"
     :output-to "js/main.js"
     :source-map true
     :asset-path "/js"
     })

Javascript file:

goog.provide("test.Test");

test.Test = function(x) {
  this.x = x;
};


 Comments   
Comment by Mike Fikes [ 05/Feb/16 1:51 PM ]

Hi Dominykas, is the absolute path intentional? I suspect the intent was to not have the leading /.

Comment by Dominykas Mostauskis [ 05/Feb/16 2:01 PM ]

I made this typo when posting. On my setup paths are relative to project root.

Comment by David Nolen [ 05/Feb/16 2:38 PM ]

As far as I know quite a few people rely on this functionality. Please provide a complete minimal example or this issue will be closed. All source should be in the ticket or in this comment thread, no external links. Thanks.

Comment by Dominykas Mostauskis [ 05/Feb/16 3:50 PM ]

Can't reproduce. Tips would be appreciated. Banging my head against the wall here.





[CLJS-1556] Invalid code emit for obj literal Created: 31/Jan/16  Updated: 31/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS-1556.patch    
Patch: Code and Test

 Description   

For some legal ClojureScript expressions involving #js object literals, invalid JavaScript is emitted. Specifically this has to do with object literals appearing at the beginning of statements where the opening brace can be interpreted as the beginning of a JavaScript block.

One way to reproduce this is to evaluate

(do #js {:a 1} 
    #js {:b 2})

in the Node REPL. In this case it is the first object literal that causes the problem; the second one emitted follows a return statement and is OK.

Rationale for marking it as minor: This appears to only really occur in places where the object literal won't actually be used.






[CLJS-1548] cannot reference a JS global var with same name as current namespace Created: 19/Jan/16  Updated: 19/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Shaun LeBron Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Steps to reproduce:

1. Create a JS global var `foo`.
2. Create a `foo.core` namespace.
3. Try referencing `js/foo` in `foo.core`.

Problem:

`js/foo` does not resolve to the JS global var `foo`
(in advanced optimizations, but not w/ optimizations off).

Solution: Proposing we issue a warning.

Will put together a diagnostic project soon and see about working on a patch.






[CLJS-1544] cljs.test REPL reload support Created: 13/Jan/16  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.228
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

When adding a test to a test ns that uses cljs.test and re-loading (via require + :reload) that namespace in the REPL after saving the file - invoking run-tests does not include the newly added test.






[CLJS-1543] Support Closure libs using goog.module Created: 12/Jan/16  Updated: 13/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Juho Teperi Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

goog.module is a new way to define Closure namespaces: https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide

It is used for example in https://github.com/google/incremental-dom

I didn't do full check of how Closure libraries are handled, but one function which is definitely used by cljs.closure is cljs.js-deps/find-classpath-lib which calls cljs.js-deps/parse-js-ns to read a JS file and parse module information from it. Currently the function reads lines before first function declaration and uses a regex to find goog.provide and goog.require calls. Probably Closure Compiler has some built-in functionality to parse files which could be leveraged.

Besides reading module information from files, another question is if using goog.module defined namespaces for traditional/legacy namespaces generated by ClojureScript compiler needs something special. When goog.module is required, goog.require returns the exported object but no global is set. There is however a function to create the globals: https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide#how-do-i-use-a-googmodule-from-a-traditional-closure-file

Notes:

  • Can we still assume that goog.requires all occur before first function declaration?
    • Would be fixed by using possible Closure Compiler functionality
    • Class com.google.javascript.jscomp.deps.JsFileParser looks promising
  • "GCL hasn't switched to it so it may be something driven by some users not something that Google uses more broadly" (David at slack)





[CLJS-1540] Arity-1 version of js->clj should pass keyword arguments for default options, as expected by js->clj Created: 07/Jan/16  Updated: 04/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Nicolás Berger Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File js-clj-keyword-opts.patch    
Patch: Code

 Description   

Arity-1 version of js->clj is passing the map {:keywordize-keys false} as the default options of js->clj, when it should pass the keyword arguments :keywordize-keys false. It's working by luck, because keywordize-keys ends being nil by default, which is falsey. But it's confusing for anyone reading the code and trying to pass {:keywordize-keys true} with the expectation that it would keywordize the keys.

References: http://dev.clojure.org/jira/browse/CLJS-750 and https://groups.google.com/forum/#!topic/clojurescript/Dis6845WL5U



 Comments   
Comment by Mike Fikes [ 29/Jan/16 7:59 PM ]
Comment by David Nolen [ 04/Feb/16 4:05 PM ]

Patch looks OK, Nicolas have you submitted your CA? Thanks.

Comment by Nicolás Berger [ 04/Feb/16 5:00 PM ]

Yes I did, David





[CLJS-1536] REPL def symbol init collision Created: 03/Jan/16  Updated: 04/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

In a REPL, if you try def where the init is a local matching the symbol being defined, then analysis fails.

(let [x 1]
  (def x x))

This can be verified in script/noderepljs and you can see it is some bad interaction with REPL var emission because if :def-emits-var false is added to the script, things work.



 Comments   
Comment by Txus Bach [ 04/Jan/16 3:01 PM ]

Confirmed that evaluating this breaks:

(require 'clojure.tools.reader
         'cljs.analyzer
         'cljs.compiler)

(let [env '{:ns {:name cljs.user}
            :def-emits-var true
            :locals {}}]
  (->> "(let [x 1] (def x 3))"
       clojure.tools.reader/read-string
       (cljs.analyzer/analyze env)
       cljs.compiler/emit-str))

(When emitting, not when just analyzing.)

Removing `:def-emits-var true` makes the bug disappear.

Looking into how to fix it – any clues are welcome, it's my first time around the codebase

Comment by Txus Bach [ 04/Jan/16 3:45 PM ]

Seems that var-ast is returning nil, because resolve-var doesn't return a map with an :ns key. Not sure if this code should work, but it returns nil:

(let [env {:ns {:name 'cljs.user},
 :def-emits-var true,
 :locals
 {'x
  {:init
   {:op :constant,
    :env
    {:ns {:name 'cljs.user},
     :def-emits-var true,
     :locals {},
     :line nil,
     :column nil,
     :context :expr},
    :form 1,
    :tag 'number},
   :name 'x,
   :binding-form? true,
   :op :var,
   :env {:line nil, :column nil},
   :column nil,
   :line nil,
   :info {:name 'x, :shadow nil},
   :tag 'number,
   :shadow nil,
   :local true}}}]
  (ana/resolve-var env 'x))

Will continue tomorrow. It's so much fun!





[CLJS-1519] Collection invoke errors off by 1 Created: 22/Dec/15  Updated: 19/Apr/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Mike Jackson
Resolution: Unresolved Votes: 0
Labels: newbie


 Description   

Runtime collection invokes will report arity that is off by one. This is because we use the generic function arity dispatching logic which doesn't account for the 1st self argument.



 Comments   
Comment by Mike Jackson [ 15/Apr/16 1:02 AM ]

Hey David,

Can I pick this one up? I'm a first time contributor and I wouldn't mind using this to get a lay of the land. I've already signed the Contributor Agreement.

Cheers

Comment by David Nolen [ 19/Apr/16 2:12 PM ]

Mike, I've updated your permissions. Please assign the ticket to yourself. Thanks!

Comment by Mike Jackson [ 19/Apr/16 2:42 PM ]

Awesome, thanks. Looking forward to it.





[CLJS-1518] Case macro expansion evaluates expression twice Created: 21/Dec/15  Updated: 31/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145, 1.7.228
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Darrick Wiebe Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None
Environment:

The issue is present in version 1.7.189.


Attachments: Text File CLJS-1518.patch    
Patch: Code and Test

 Description   

The value being checked is evaluated twice if all of the test values are keywords.

(macroexpand-1 '(case (expensive) :a 1 2))
(cljs.core/let [G__123555 (if (cljs.core/keyword? (expensive)) (.-fqn (expensive)) nil)]
  (case* G__123555 [["a"]] [1] 2))


 Comments   
Comment by Mike Fikes [ 31/Jan/16 11:38 PM ]

Patch takes advantage of the existing gensym as a temp place to stash the evaluated value before test / FQN conversion.

Adds a unit test specifically checking for single evaluation in this case.

Comment by Mike Fikes [ 31/Jan/16 11:40 PM ]

With the patch, Darrick's macroexpansion example becomes:

(cljs.core/let [G__7663 (expensive) 
                G__7663 (if (cljs.core/keyword? G__7663) (.-fqn G__7663) nil)] 
  (case* G__7663 [["a"]] [1] 2))




[CLJS-1515] Self-host: Allow :file key in cljs.js/*load-fn* callback Created: 17/Dec/15  Updated: 14/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: bootstrap

Attachments: Text File CLJS-1515-1.patch     Text File CLJS-1515-2.patch     Text File CLJS-1515-3.patch     Text File CLJS-1515-4.patch     Text File CLJS-1515-5.patch     Text File CLJS-1515-6.patch    
Patch: Code and Test

 Description   

Bootstrapped ClojureScript is abstracted away from direct I/O by use of a *load-fn* callback. A result is that when a namespace is loaded, the :file attribute associated with def s in [:cljs.analyzer/namespaces 'foo.ns :defs] in the AST is nil, because cljs.analyzer/*cljs-file* cannot be set to a meaningful value.

This ticket asks for an extension to *load-fn*, allowing a :file key to be optionally included by cljs.js clients, and for cljs.analyzer/*cljs-file* to be bound to that value in appropriate places in cljs.js so that the :file info appears in the AST.

One rationale for this :file attribute is that it makes it easier for clients of cljs.js to look up the file for a def, say, for use when implementing a source REPL special, for example.



 Comments   
Comment by Andrea Richiardi [ 17/Dec/15 4:31 PM ]

Initial patch, adding a :file key to load-fn and a :file-env key inside opts and then assigning it to cljs.analyzer/cljs-file in eval-str. This approach can be discussed and we can create an ad-hoc function for binding. It felt right there.
Moreover, cljs.analyzer/cljs-file gets overridden every time with the payload coming from load-fn.
All this was very quickly done in order to have a feedback from who's more expert than me about the consequences. This is also my very first ClojureScript patch

Comment by Mike Fikes [ 17/Dec/15 5:33 PM ]

I tried this patch. It is working fine for me when loading namespaces, but if I use cljs.js/analyze-str where the string is an ns form referring other namespaces loaded via *load-fn*, along with a def, things are off. (I have that ns referring macros from a clj file and a symbol from a cljs file, and the clj file gets associated with the top-level def and the macro, and the def in the referred file ends up with nil.

As a minor aside, the patch has a spurious whitespace change at the end.

Comment by Mike Fikes [ 17/Dec/15 5:56 PM ]

With respect to the last comment: The patch employs the pattern of conveying the :file passed in the cb via a :file-env opt to the consuming fn. It is consumed in eval-str* but not in analyze-str*. If the same logic is added to analyze-str* then the problem mentioned in the last comment goes away.

Comment by David Miller [ 17/Dec/15 6:48 PM ]

I'm hopeful someone will assign this to a responsible party. I am not that person.

Comment by Andrea Richiardi [ 17/Dec/15 7:21 PM ]

sorry David (Miller) and thanks Mike, I will rework it, adding some tests as well

Comment by Andrea Richiardi [ 17/Dec/15 7:23 PM ]

By the way this makes me think that maybe a better choice is to consider this a side effect and directly modify cljs.analyzer/*cljs-file* returning from *load-fn*, who knows how many other spots I am not covering...

Comment by Mike Fikes [ 18/Dec/15 5:36 AM ]

Two more comments:

1) Broadening the scope of the binding doesn't appear to work properly for me. But things do work if the bindings are done as in the patch now (next to where the other bindings are done).

2) Perhaps :file should be only set if the :lang being called back with is :clj. Maybe this could at least be documented. (It is not clear to me if it is useful for :js, as the patch is setting ana/*cljs-file*.)

Comment by Andrea Richiardi [ 18/Dec/15 10:27 AM ]

About 2), is any AST generated for .js files at all? If yes maybe then we should add it too...I need to explore that code path as well.

Comment by Andrea Richiardi [ 18/Dec/15 3:33 PM ]

So basically with ana/*cljs-file* binding the :file in :meta is not changed at all (I fixed following Mike's advice) but :file is, are we ok with this? In replumb (from planck) we check both so no problem, nonetheless it would be great to know why..

:defs {foo {:protocol-inline nil, :meta {:file bootstrap-test.core, :line 3, :column 7, :end-line 3, :end-column 10, :arglists (quote ([a b]))}, :name bootstrap-test.core/foo, :variadic false, :file /.../clojurescript/src/test/self/bootstrap_test/core.cljs, :end-column 10, :method-params ([a b]), :protocol-impl nil, :arglists-meta (nil nil), :column 1, :line 3, :end-line 3, :max-fixed-arity 2, :fn-var true, :arglists (quote ([a b]))}}, :require-macros nil, :doc nil

Comment by Andrea Richiardi [ 18/Dec/15 3:44 PM ]

It looks like the information in :meta comes directly from the multimethod parse which I dont' think we can change easily. So either we override :file in :meta or we leave as it is with a note in the documentation for :file in *load-fn*

Comment by Andrea Richiardi [ 18/Dec/15 4:10 PM ]

About :js files at least to me it looks like the only trace of importing, say, goog.sting in the AST is in the :imports of the parent namespace. No :file key anywhere, but please correct me if I am wrong as the AST is difficult to untangle

Comment by Andrea Richiardi [ 18/Dec/15 5:29 PM ]

Patch and test

Comment by Mike Fikes [ 18/Dec/15 7:43 PM ]

Comments on {{CLJS-1515-2.patch}} (mostly just opinion):

  1. (Opinion): Introduces new public API, especially with respect to AST exposure. Perhaps fn could instead be added to the test namespace.
  2. (Opinion): I wouldn't try anything complicated to try to patch up the :file that is in the :meta map. (Maybe we'll ultimately figure out why setting cljs.analyzer/*cljs-file* is insufficient for that bit.)
  3. (Opinion): For the :file docstring, I'd avoid mentioning AST. (Even though that was the true motivation for this ticket.) I'd only indicate that it represents the location where :source was obtained. (Which I guess would leave open it being perfectly fine for clients to provide it in the case that :lang is :js.)
  4. script/test-self-host passes for me.
  5. Inadvertent whitespace changes in append-source-map.
Comment by Andrea Richiardi [ 18/Dec/15 7:49 PM ]

1. Sorry Mike I don't understand when you say fn...what do you mean? Can you expand?
2. Yes and it would change a lot of code, that's why I didn't even try
3. Ok can change that, but where should be mentioned that we are modifying :file but not inside :meta?
4. Great!
5. You know I really tried hard not to have that, I will try again to disable all the auto indent my emacs has.

Comment by Mike Fikes [ 18/Dec/15 8:30 PM ]

1. The three new public functions in cljs.js: (var-ast, ns-ast, file->lang) could perhaps be moved to be utility functions in the self-host test namespace.
3. Dunno about the :meta question. But on the :lang :js question, perhaps the patch should only bind :cljs.analyzer/*cljs-file* if :lang :clj?

Comment by Andrea Richiardi [ 18/Dec/15 8:38 PM ]

1. I know it looks like they are used in test only, but I put them there as public because both replumb and planck use them and I was kind of "proposing" this kind of AST utils to be part of the official API (so that the poor dev does not have to go through cljs.analyzer in order to query the AST. I understand if no though.
3. This I don't really know, and seek guidance. I have not noticed any significant change in the AST for .js file, maybe *cljs-file* is never queried in that code path. I could not even find a way to test it. But I could, of course, be very wrong.

Comment by Andrea Richiardi [ 21/Dec/15 2:13 PM ]

This puts the utils functions in the test namespace for now, maybe thinking about exposing some API in the future.

Comment by Andrea Richiardi [ 21/Dec/15 8:19 PM ]

About :js:

  • it looks like the analyze-str code path simply recurs to fetch the next dep. So I guess that branch does not touch the AST.
  • for the require code path it looks like it -> is -> similar.

Therefore I don't see the point in adding :file for :js and I will not bind *cljs-file* if this is the case, as you suggested.

Comment by Andrea Richiardi [ 21/Dec/15 9:48 PM ]

Patch #4 changes the conveying key to :cljs-file, after Mike's good suggestion, and moves the assoc to the (condp ... :clj) branch only. I also added a test to check that *cljs-file* does not match the file path when in the :js branch.

Comment by Andrea Richiardi [ 21/Dec/15 11:56 PM ]

Another note, the *cljs-file* test works because the binding form does not actually restore the old value when it exits...In Clojure it would not probably work.

^ This is plain wrong, I was not considering the "when" my tests are executed, please disregard.

Comment by Mike Fikes [ 23/Dec/15 5:13 PM ]

CLJS-1515-4.patch LGTM.

Details: I tested against current ClojureScript master, using downstream Planck to load regular and macro namespaces and the :file portion of the AST gets properly updated. This also occurs if I instead use cljs.js/analyze-str passing in an ns form that causes code to be loaded. Additionally unit tests (regular and bootstrap) pass for me. I think this patch is functionally good to go.

Comment by David Nolen [ 26/Dec/15 6:54 AM ]

Copying goog.string into the source tree is not desirable. Please fix the tests to remove this. If you must, copy it to a temporary a location from the Google Closure Library JAR and remove it after the test has completed, thanks.

Comment by Andrea Richiardi [ 26/Dec/15 2:20 PM ]

Patch 5 avoid copying string.js and re-uses self_host/test.js.

Comment by Andrea Richiardi [ 26/Dec/15 2:22 PM ]

Done what you asked

Comment by Mike Fikes [ 05/Feb/16 8:05 PM ]

CLJS-1515-5.patch no longer applies

Comment by Andrea Richiardi [ 14/Feb/16 9:17 PM ]

Reapplied and re-tested. Works

Testing with Node

Testing self-host.test

Ran 8 tests containing 47 assertions.
0 failures, 0 errors.




[CLJS-1513] Javascript emitted for cljs.pprint is misinterpreted under Mobile Safari 7.0 Created: 14/Dec/15  Updated: 14/Dec/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.28
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Ruslan Prokopchuk Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Tested with 1.7.28 and 1.7.170 versions of ClojureScript.

browser = Mobile Safari 7.0
os = iOS 7.1.2



 Description   

Create minimal ClojureScript project, add {{(:require [cljs.pprint :refer [pprint]])}} to the source file and compile with :optimizations :advanced. Output js will contain strings like

~\x3c\x3c-(~;~@{~w~^ ~_~}~;)-\x3c~:\x3e
which lead to the following error in the Mobile Safari 7.0:

Error: Directive "{" is undefined
~<<-(~;~@{~w~^ ~_~}~;)-<~:>
         ^





[CLJS-1502] Browser REPL broken when started with :optimizations :none Created: 05/Dec/15  Updated: 06/Dec/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: ewen grosjean Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File cljs-1502.patch    
Patch: Code

 Description   

Creating a browser-repl env like this: (cljs.repl.browser/repl-env :optimizations :none) does not work because client.js is not compiled in a single file. The browser throws the following error: goog is not defined.
:optimizations :simple (the default) works fine.
A quick fix would be to force :optimizations :simple in the REPL options. However, being able to set :optimizations :none would probably speed up compilation times.






[CLJS-1495] Internal ast? assertion for var in fn in def Created: 28/Nov/15  Updated: 29/Nov/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Also affects 170 (not in pulldown yet)
Same behavior in regular and bootstrapped



 Description   

This form, when issued at the REPL, fails to compile, triggering an ana/ast? :pre violation:

(def f (fn [] #'f))

There appears to be nothing fundamentally wrong with the construct, as it can be worked around in multiple ways.

These workarounds, which move the var special out, succeed:

(def f (let [vf #'f] (fn [] vf)))
(declare f)
(let [x (fn [] #'f)] (def f x))

Also, these subtler workarounds succeed in avoiding the issue:

(def f (fn [] #'cljs.user/f))
(def f (fn x [] #'f))


 Comments   
Comment by Mike Fikes [ 28/Nov/15 7:49 PM ]

Analysis of the issue:

tl;dr: A synthetic local is shadowing the top-level var that the var special is being applied to.

Detailed analysis:

The form

(def f (fn [] #'f))

can be reduced to this simpler self-named form to consider, which also exhibits the issue:

(fn f [] #'f)

Analyzing (def f (fn [])) will show that :fn-self-name true is set.

A consequence is that the self name, f in the running example, is carried into the function body as a local symbol, and thus f is no longer a symbol resolving to the top-level var. This leads to a analyzing code that is, in essence, like this sequence

(declare g)
#'g ;; OK
(let [g 1] #'g) ;; exhibits error

Another way of saying the above: The locally introduced self-name, which is otherwise fine with respect to self-recursion, thwarts the var special in this situation, effectively shadowing the top-level var.

Comment by Mike Fikes [ 29/Nov/15 11:08 AM ]

A similar situation occurred for Clojure and that it defeated memoization until fixed http://dev.clojure.org/jira/browse/CLJ-809

Given this

(defn fib [n]
  (if (< n 2)
    n
    (+ (fib (dec n))
      (fib (dec (dec n))))))

Clojure takes a few seconds to compute (fib 41) but is instantaneous after (def fib (memoize fib)).

The synthetic local defeats this attempt at memoization in ClojureScript.





[CLJS-1494] turn cljs.core/*assert* into a goog-define Created: 25/Nov/15  Updated: 22/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Thomas Heller Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File goog-define-assert.patch    
Patch: Code

 Description   

This patch turns the cljs.core/*assert* boolean into a goog.define and also checks *assert* at runtime (instead of only at compile-time).

The closure define option allows the closure compiler to eliminate asserts in :advanced, while :none builds can keep the asserts. This is one of the few remaining issues that prevent :advanced builds to re-use :none compiled (cached) files.

:elide-asserts is unaffected to keep this as simple as possible, but could be built on top of the goog.define instead of actually affecting the compiled output.



 Comments   
Comment by Mike Fikes [ 20/Feb/16 8:02 AM ]

Patch no longer applies, probably owing to CLJS-970.

Comment by Thomas Heller [ 22/Feb/16 5:08 AM ]

There was one more issue I discovered with my approach. My goal was to enable the Closure Compiler to eliminate the asserts when using :advanced compilation. This works perfectly fine with using a goog.define for *assert* but the compiler will complain if you try to adjust the define later since goog.define vars are not allowed to be adjusted at runtime.

(binding [*assert* false]
  (something-that-asserts))

This works in CLJ but not in CLJS since *assert* is only checked at compile time. If compiled with :elide-asserts true you can't bind assert to true either since the code no longer exists.

So some compromise must be made either way, the best solution IMHO would be to have a goog.define which lets the compiler decide whether to eliminate the asserts or not, independent from the *assert* and then moving the assert check itself into js instead of the compiler.

Happy to write the patch if interested.





[CLJS-1490] Watch macro files in cljs.build.api/watch Created: 23/Nov/15  Updated: 24/Nov/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Sebastian Bensusan Assignee: Sebastian Bensusan
Resolution: Unresolved Votes: 0
Labels: None


 Description   

The cljs.build.api/watch function is great for writing scripts or tooling but it doesn't track .clj macro files as noted in here. Direct users face unexpected behavior and tooling developers need to reimplement the watch functionality.

The undesired behavior can be tested with this repo (instructions there).

I propose to add the functionality into the watch function, or at least propose a mechanism so that tooling authors can reuse the functionality.



 Comments   
Comment by David Nolen [ 24/Nov/15 12:00 PM ]

Happy to see a fix for this.





[CLJS-1485] Error when requiring `goog` namespace in a ns declaration Created: 10/Nov/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Avicenna Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

I wanted to use functions from goog namespace although--as I found out later, I didn't have to because goog is already exists in my namespace. So, I put (:require [goog]) in a ns declaration. Then, when I tried to reload that particular namespace by doing :require :reload in a cljs repl, I got:

Error: Namespace "x.x.x" already declared.

Doing :require :reload again in the cljs repl makes the repl throws

Error: Namespace "cljs.user" already declared.
(NO_SOURCE_FILE)
(out/goog/base.js:273:40)

I tested the steps below using clojurescript 1.7.145 and 1.7.170.

Here are the steps to reproduce which are taken from clojurescript quickstart-browser repl section:

1. Download the standalone clojurescript 1.7.170 jar https://github.com/clojure/clojurescript/releases/download/r1.7.170/cljs.jar

2. Create a directory hello_world and copy the JAR into that directory, then from inside the hello_world directory:

mkdir -p src/hello_world;touch repl.clj;touch index.html;touch src/hello_world/core.cljs

3. repl.clj content

(require 'cljs.repl)
(require 'cljs.build.api)
(require 'cljs.repl.browser)

(cljs.build.api/build "src"
  {:main 'hello-world.core
   :output-to "out/main.js"
   :verbose true})

(cljs.repl/repl (cljs.repl.browser/repl-env)
  :watch "src"
  :output-dir "out")

4. index.html content

<html>
    <body>
        <script type="text/javascript" src="out/main.js"></script>
    </body>
</html>

5. src/hello_world/core.cljs content

(ns hello-world.core
  (:require [clojure.browser.repl :as repl]))

(defonce conn
  (repl/connect "http://localhost:9000/repl"))

(enable-console-print!)

(println "Hello world!")

(defn foo [a b]
  (+ a b))

6. run clojurescript repl

java -cp cljs.jar:src clojure.main repl.clj

7. Open http://localhost:9000 in browser (I use google chrome). Open javascript console.

8. enter expression below in the clojurescript repl

(require '[hello-world.core :as hello] :reload)

10. Look the browser javascript console. Nothing new shown.

11. Quit from the repl using :cljs/quit

12. Add [goog] in ns declaration in src/hello_world/core.cljs so that the content of the file becomes:

(ns hello-world.core
  (:require [clojure.browser.repl :as repl]
            [goog]))

(defonce conn
  (repl/connect "http://localhost:9000/repl"))

(enable-console-print!)

(println "Hello world!")

(defn foo [a b]
  (+ a b))

13. Run the clojurescript repl again

java -cp cljs.jar:src clojure.main repl.clj

14. Now refresh the http://localhost:9000 in browser. Make sure the javascript console stays open.

13. enter expression below in the clojurescript repl

(require '[hello-world.core :as hello] :reload)
;;=> nil

it just returns nil

15. See the javascript console, it shows

Uncaught Error: Namespace "hello_world.core" already declared.

16. Executing this expression again (require '[hello-world.core :as hello] :reload) shows nothing new in the browser's javascript console while the clojurescript repl throws

Error: Namespace "cljs.user" already declared.
(NO_SOURCE_FILE)
(out/goog/base.js:273:40)





[CLJS-1474] Warn if reserved symbol is defined Created: 21/Oct/15  Updated: 12/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Martin Klepsch Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: newbie


 Description   

Currently a definition like

(defn set! [] ...)

will not cause any warning. Any usage of it (without :as namespace aliasing) however will not use the defined var but the set! special form.

A warning seems appropriate.






[CLJS-1473] Require fail on ns/in-ns created namespaces Created: 20/Oct/15  Updated: 21/Oct/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Andrea Richiardi Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug
Environment:

3.13.0-65 x86_64 GNU/Linux



 Description   

Just to report that the following does not work at the repl (in Clojure it does):

(ns first.namespace)
(def a 4)
(ns second.es)
(require 'first.namespace) ;; with :reload is the same
java.lang.IllegalArgumentException: Namespace first.namespace does not exist
	at cljs.closure$source_for_namespace.invoke(closure.clj:605)
	at cljs.repl$load_namespace.invoke(repl.cljc:182)
	at cljs.repl$load_dependencies.invoke(repl.cljc:206)
	at cljs.repl$evaluate_form.invoke(repl.cljc:474)
	at cljs.repl$fn__4470$self__4482.invoke(repl.cljc:673)
	at clojure.lang.AFn.applyToHelper(AFn.java:165)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.core$apply.invoke(core.clj:630)
	at cljs.repl$wrap_self$g__4450.invoke(repl.cljc:630)
	at cljs.repl$repl_STAR_$read_eval_print__4536.invoke(repl.cljc:854)
	at cljs.repl$repl_STAR_$fn__4542$fn__4551.invoke(repl.cljc:895)
	at cljs.repl$repl_STAR_$fn__4542.invoke(repl.cljc:894)
	at cljs.compiler$with_core_cljs.invoke(compiler.cljc:1146)
	at cljs.repl$repl_STAR_.invoke(repl.cljc:858)
	at cljs.repl$repl.doInvoke(repl.cljc:976)
	at clojure.lang.RestFn.invoke(RestFn.java:410)
	at cljsbuild.repl.rhino$run_repl_rhino.invoke(rhino.clj:8)
	at user$eval4946.invoke(form-init5490341798700416710.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:6782)
	at clojure.lang.Compiler.eval(Compiler.java:6772)
	at clojure.lang.Compiler.load(Compiler.java:7227)
	at clojure.lang.Compiler.loadFile(Compiler.java:7165)
	at clojure.main$load_script.invoke(main.clj:275)
	at clojure.main$init_opt.invoke(main.clj:280)
	at clojure.main$initialize.invoke(main.clj:308)
	at clojure.main$null_opt.invoke(main.clj:343)
	at clojure.main$main.doInvoke(main.clj:421)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at clojure.lang.Var.invoke(Var.java:383)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.Var.applyTo(Var.java:700)
	at clojure.main.main(main.java:37)

I am available for investigating more and/or taking charge of the issue.



 Comments   
Comment by Andrea Richiardi [ 20/Oct/15 9:31 PM ]

sorry about the mistakes...how to edit?

Comment by Andrea Richiardi [ 21/Oct/15 1:32 PM ]

Confirmed using NodeJs repl:

ClojureScript Node.js REPL server listening on 51885
Watch compilation log available at: out/watch.log
To quit, type: :cljs/quit
cljs.user=> (in-ns 'ns.core)
nil
ns.core=> (def a 3)
#'ns.core/a
ns.core=> (in-ns 's.core.repl)
nil
s.core.repl=> (require 'ns.core)
java.lang.IllegalArgumentException: Namespace ns.core does not exist
	at cljs.closure$source_for_namespace.invoke(closure.clj:605)
	at cljs.repl$load_namespace.invoke(repl.cljc:182)
	at cljs.repl$load_dependencies.invoke(repl.cljc:206)
	at cljs.repl$evaluate_form.invoke(repl.cljc:474)
	at cljs.repl$fn__4583$self__4595.invoke(repl.cljc:673)
	at clojure.lang.AFn.applyToHelper(AFn.java:165)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.core$apply.invoke(core.clj:630)
	at cljs.repl$wrap_self$g__4563.invoke(repl.cljc:630)
	at cljs.repl$repl_STAR_$read_eval_print__4649.invoke(repl.cljc:854)
	at cljs.repl$repl_STAR_$fn__4655$fn__4664.invoke(repl.cljc:895)
	at cljs.repl$repl_STAR_$fn__4655.invoke(repl.cljc:894)
	at cljs.compiler$with_core_cljs.invoke(compiler.cljc:1146)
	at cljs.repl$repl_STAR_.invoke(repl.cljc:858)
	at cljs.repl$repl.doInvoke(repl.cljc:976)
	at clojure.lang.RestFn.invoke(RestFn.java:486)
	at user$eval44.invoke(node_repl.clj:10)
	at clojure.lang.Compiler.eval(Compiler.java:6782)
	at clojure.lang.Compiler.load(Compiler.java:7227)
	at clojure.lang.Compiler.loadFile(Compiler.java:7165)
	at clojure.main$load_script.invoke(main.clj:275)
	at clojure.main$script_opt.invoke(main.clj:337)
	at clojure.main$main.doInvoke(main.clj:421)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.lang.Var.invoke(Var.java:379)
	at clojure.lang.AFn.applyToHelper(AFn.java:154)
	at clojure.lang.Var.applyTo(Var.java:700)
	at clojure.main.main(main.java:37)
Comment by Andrea Richiardi [ 21/Oct/15 1:37 PM ]

I guess it is each repl-env with its -evaluate to be responsible for loading right? Can it be a proble of each and every *load-fn* out there?

Comment by Andrea Richiardi [ 21/Oct/15 2:37 PM ]

I am analyzing `cljs.repl/load-namespace` and it looks like it handles only loading from sources:

line 178 @ clojurescript 1.7.158

 sources (cljsc/add-dependencies
                   (merge (env->opts repl-env) opts)
                   {:requires [(name ns)]
                    :type :seed
                    :url (:uri (cljsc/source-for-namespace
                                 ns env/*compiler*))})

The function `cljs.closure/source-for-namespace` is throwing the exception because of course it cannot find the sources for a manually created namespace.
An idea could be to change `cljs.closure/source-for-namespace` and return nil in case no sources can be found.
Then change `cljs.repl/load-namespace` to check for created namespaces when `souces` is nil.

Mike told me that the namespaces are not reified, so the next question is, is there an atom that contains all the created namespaces?

I am going to wait for input on this, when everybody has time of course.





[CLJS-1458] re-matches might give false negative when using match groups Created: 25/Sep/15  Updated: 31/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Nikita Prokopov Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File cljs-1458-re-matches-might-give-false-negative.patch    
Patch: Code and Test

 Description   

Current behaviour:

(re-matches #"(a|aa)" "aa") => nil

Expected:

(re-matches #"(a|aa)" "aa") => ["aa" "aa"]

JVM version works as expected, only CLJS is affected



 Comments   
Comment by David Nolen [ 14/Oct/15 11:36 AM ]

This is the kind of ticket that tends to break existing code. We should get some people who are interested in this ticket to actually try it out.

Comment by Mike Fikes [ 31/Jan/16 3:45 PM ]

FWIW, I gave cljs-1458-re-matches-might-give-false-negative.patch a try in bootstrapped ClojureScript and it is working fine there (each of the additional unit tests produce the expected results in bootstrapped).





[CLJS-1453] cljs.compiler/load-libs does not preserve user expressed require order Created: 17/Sep/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Due to putting the requires into a map the original order is lost. This is a problem primarily when order specific side effects are present in the required namespaces.






[CLJS-1448] lib-rel-path fails on Windows because of File/separator being \\ Created: 14/Sep/15  Updated: 12/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Orlando William Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug, newbie
Environment:

Windows



 Description   

https://github.com/clojure/clojurescript/blob/cc953d4be7b4a256fd5eae783f9106a2929a4126/src/main/clojure/cljs/closure.clj#L1210

That code calls replace with \ on windows, who ends up calling (.replaceAll "foo.js" "." "\") and fails with
IllegalArgumentException character to be escaped is missing java.util.regex.Matcher.appendReplacement (:-1)



 Comments   
Comment by Orlando William [ 14/Sep/15 1:20 PM ]

I can't find a way to edit the description, I meant \ followed by \ but somehow it got a line break

Comment by David Nolen [ 12/Feb/16 4:12 PM ]

A patch for this is welcome.





[CLJS-1444] Node.js shim requires `node` invocation to be in the same directory as shim script Created: 10/Sep/15  Updated: 14/Mar/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Martin Klepsch Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Don't have time to provide a proper repro now but the basic issue can be illustrated by this:

~/c/boot-cljs-example (master=) node target/main.js
module.js:338
    throw err;
          ^
Error: Cannot find module '/Users/martin/code/boot-cljs-example/out/goog/bootstrap/nodejs.js'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:278:25)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)
    at Object.<anonymous> (/Users/martin/code/boot-cljs-example/target/main.js:6:1)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
~/c/boot-cljs-example (master=) cd target/
~/c/b/target (master=) node main.js
Starting...

This is compiled with boot because that was what I had at hand right now. The compiled shim looks like this:

var path = require("path");
try {
    require("source-map-support").install();
} catch(err) {
}
require(path.join(path.resolve("."),"out","goog","bootstrap","nodejs.js"));
require(path.join(path.resolve("."),"out","cljs_deps.js"));
goog.global.CLOSURE_UNCOMPILED_DEFINES = {"cljs.core._STAR_target_STAR_":"nodejs"};
goog.require("boot.cljs.main");
goog.require("cljs.nodejscli");

The problem here is that path.resolve(".") will return the directory the node command was invoked in and not the directory of the shim. (See the "Cannot find module..." error above)

A solution could be to use __dirname which always resolves to the directory of the current file. This might result in some breakage for existing setups.



 Comments   
Comment by Sebastian Bensusan [ 14/Oct/15 11:31 AM ]

I have a proposed solution but I fear that supporting "run from anywhere" adds essential complexity to the resulting code. My thought process:

1. Relative paths are desirable to produce "context free code." If the user chooses absolute paths, then that behavior is respected and there's nothing to handle (no "path algebra") .

2. When dealing with relative paths, the whole system needs to establish a "frame of reference", a root path. The ClojureScript compiler assumes the path from which it is compiling to be that frame of reference, which usually coincides with the top root of the project. Though arbitrary, it is the only choice that makes sense.

3. The frame of reference is not explicit anywhere in the code, since it is defined as ".". If it were explicit, it would reveal context, as in "/home/some-user/their-folder/this-project/".

4. When we approach the code from another reference point (executing the script from another directory), we first need to find the original compiler path (reference point,) and then resolve all paths from there. The compiler uses `cljs.closure/path-relative-to` for this purpose.

Path algebra:
compiler-path = __dirname - output-to

Node.js

var compiler-path = __dirname.replace(output-to, "")
path.resolve (compiler-path, output-dir, "goog", "base.js")
path.resolve (compiler-path, output-dir, "cljs_deps.js")

which assumes that if output-to was given relatively, then output-dir is also relative. If they are not in sync, more work needs to be done to keep them that way.

It's not up to me to decide if the extra complexity is worth the use-case. I actually hope there is a simpler solution to solve this that I'm not seeing.

Comment by Karol Majta [ 14/Mar/16 10:43 AM ]

I find this behavior really weird and would opt for switching to __dirname. I am also not sure i fully understand consequences of such switch (I have little cljs experience, speaking more from the perspective of nodejs user). My point is: current behavior renders clojurescript hard to use for commandline and desktop applications (namely electron).

For command line and desktop applications assumptions about CWD cannot be made. For now i run my applications through a bootstrap script JS script:

process.chdir(__dirname);
require('./target/out');

I am lucky that my code does not have to use the real CWD, but it's a hack more than a real solution.

Speaking from nodejs perspective:

1. Using absolute paths is considered a bad practice anyway.
2. Nodejs programs that don't use external packages (don't depend on node_modules) can be run from any CWD
3. Nodejs programs that do depend on node_modules will break if run from a different directory than the one containing node_modules, but this is expected behavior.





[CLJS-1428] Add a cljs.core/*command-line-args* var Created: 16/Aug/15  Updated: 01/Sep/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None


 Description   

Add a cljs.core/*command-line-args* var that mimics Clojure's:

https://github.com/clojure/clojure/blob/4bb1dbd596f032621c00a670b1609a94acfcfcab/src/clj/clojure/core.clj#L6148

Rationale:
1) Simplifies writing command-line scripts in ClojureScript if this var is universally available.
2) Consistency with Clojure.

Existing tooling can ignore the var (it would be initialized with nil).

Command-line REPLs or other command-line environments can bind a sequence to this var when launched.



 Comments   
Comment by Justin Thomas [ 31/Aug/15 10:14 PM ]

In this file: https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/nodejs.cljs Looks like you could set this: (def *command-line-args* (.-argv process)) and it'd be accessible under nodejs/*command-line-args*. Not sure where the star vars are set in the elsewhere in the code or if that's a good solution putting it in each repo.

Maybe get the target var somehow and set it based on that--similar to how the nodejs file is loaded?

Found this: src/main/clojure/cljs/core.cljc and saw references to a ns called env.

Comment by David Nolen [ 01/Sep/15 6:44 AM ]

This ticket needs more rationale and stronger outline of the design issues before proceeding. As it stands no one should be working on this yet.

Comment by Justin Thomas [ 01/Sep/15 4:27 PM ]

Just trying to learn the code at the moment. As for rationale, using the command line vars in node is a common use case for shell scripts. Accessing the command line vars in a more clojure-y way seems like a good idea.





[CLJS-1421] Enable Asynchronous cljs.js/*eval-fn* Created: 14/Aug/15  Updated: 16/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Matthew Molloy Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: asynchronous, bootstrap


 Description   

In bootstrapped ClojureScript cljs.js/eval-fn receives javascript source and evaluates it, returning a result. In some contexts it is necessary to evaluate the js asynchronously, can we add this functionality?



 Comments   
Comment by David Nolen [ 14/Aug/15 7:49 PM ]

This ticket needs more rationale. Can you elaborate on the usecase?

Comment by Matthew Molloy [ 14/Aug/15 10:08 PM ]

My usecase is an asynchronous eval function

(fn *eval-fn*
  [{:keys [source]}]
  (js/chrome.devtools.inspectedWindow.eval source
    (fn [result err]
      (if result
        (callback result)
        (callback err))))

There must be other people who have situations like this.

Comment by David Nolen [ 16/Aug/15 12:16 AM ]

Interesting. I don't think this is a common use case, most JS engines provide synchronous eval. Not interested in any breaking changes but would be happy to take a patch that gives you the behavior you want via an option flag, :async-eval.





[CLJS-1419] enhance numeric inference, if + number? test on local var should tag local var in the successful branch Created: 12/Aug/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Comments   
Comment by David Nolen [ 12/Aug/15 6:44 AM ]

One small complication is dealing with and as it has an optimizing case.





[CLJS-1415] Handling JSDoc param name [x] optional syntax Created: 10/Aug/15  Updated: 08/Jun/16

Status: In Progress
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: type-check





[CLJS-1412] Add JSDoc type information to individual IFn methods Created: 10/Aug/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: type-check


 Description   

Propagate user supplied docstring type information to the various fn arities so that more code may be checked.






[CLJS-1407] Exposing output file dependency graph in API Created: 09/Aug/15  Updated: 08/Nov/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Juho Teperi Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Use case for boot-cljs and boot-reload:

After compilation boot-reload reloads the changed JS files. So that the files can be reloaded in correct order, boot-cljs uses dependency graph to sort the files. Currently boot-cljs accesses compiler state directly and uses data from :js-dependency-index to build the graph: https://github.com/adzerk-oss/boot-cljs/blob/0.0-3308/src/adzerk/boot_cljs/impl.clj#L17-L36

Simple solution:

If dependencies (requires) of namespace are exposed through API it is easy to build graph of cljs namespace dependencies: https://github.com/adzerk-oss/boot-cljs/blob/d479f10935be321232e2363e2ae3e9cc515a81af/src/adzerk/boot_cljs/impl.clj#L12-L32

Problem with this solution is that all-ns, ns-dependencies or target-file-for-cljs-ns do not work with foreign-deps. While foreign-dep files don't usually change and thus aren't reloaded, it's possible that user has local JS files in the project using foreign-deps and those can change.

Questions, notes and issues

  • Should cljs-dependency-graph be exposed in the API or is it enough to provide ns-dependencies and such which user can use to create dependency graph?
  • cljs.build.api/parse-js-ns can also be used to read provides and requires from compiled JS files, but this doesn't work with foreign-deps either
  • Perhaps there is some way in Closure library to reload files in correct order?
  • Supporting foreign-deps is not perhaps necessary, but if there is good way it would be nice to have.


 Comments   
Comment by Juho Teperi [ 11/Aug/15 3:18 AM ]

I would add the call to cljs.compiler.api and it could be called output-dependency-graph.

Creating the graph requires list of all the nodes and dependencies for each node. For Cljs namespaces
these are accessible through all-ns and ns analysis map :requires. Data about foreign-deps
and closure libs is available in the compiler state under :js-dependency-index key. To create the
graph we need to:

1. Get list of all nodes
2. Get dependencies for given node
3. Get output file for given node

Because steps 2 and 3 depend on the type of node, it would probably be easiest to collect those
values in step 1. So step 1 would do something like this:

{{(get-nodes ...) => {:provides "goog.net" :file "out/goog/net.js" :dependencies #{"goog.foo"}} {:provides "frontend.core" :file "out/frontend/core.js" :dependencies #{"cljs.core"}}}}

That could be implemented by concatenating data from cljs namespaces retrieved from all-ns etc. with
data from :js-dependency-index. The next and last step would be to construct the graph using reduce.

Using this implementation there would be just one new API call: output-dependency-graph.

I was thinking alternative approach with all-ns, find-ns etc. versions which would work also with foreign-deps and closure libs, but I don't think it's very easy (or efficient) e.g. to retrieve data for foreign-dep with just a name as they are indexed by file paths.

Comment by David Nolen [ 03/Nov/15 6:34 PM ]

Now that CLJS-1437 is merged what is needed to wrap this one up?

Comment by Juho Teperi [ 08/Nov/15 9:11 AM ]

My current plan with boot-cljs/boot-reload is to use Figwheel client code which uses Google Closure dependency graph for loading the files in correct order. Thus I don't need this anymore. Perhaps it's best to close this if no-one needs this currently?

Comment by David Nolen [ 08/Nov/15 9:28 AM ]

It may still be useful at some point. Will just lower the priority.





[CLJS-1390] clojure.walk treats vectors diffently from Clojure version Created: 03/Aug/15  Updated: 03/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.28
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Leon Grapenthin Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

The latest patch to clojure.walk (https://github.com/clojure/clojurescript/commit/f706fabfd5f952c4dfb4dc2caeea92f9e00d8287) ports the line of the Clojure version

(instance? clojure.lang.IMapEntry form) (outer (vec (map inner form)))

with the line

(satisfies? IMapEntry form) (outer (vec (map inner form)))

ClojureScript implements IMapEntry on any vector which I assume is intended.

In Clojure, for vectors this case falls:

(coll? form) (outer (into (empty form) (map inner form)))

This makes a difference because empty preserves metadata.
I. e.

(meta (prewalk (fn [form]
                  (vary-meta form assoc :foo true))
               []))

gives {:foo true} on earlier ClojureScript versions and Clojure, but nil on the latest version.

I have relied on this which has likely not been a very good idea, but others might have too - Hence I created this ticket for consideration.






[CLJS-1320] clojure.string/split adds separator matches & failed matches (nil) when the separator is a regex with alternation Created: 26/Jun/15  Updated: 27/Jun/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3308
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: lvh Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

I want to split a string on "; ", and optionally discard a final ";". So, I tried:

(clojure.string/split "ab; ab;" #"(; )|(;$)")

In Clojure, this does what I want:

["ab" "ab"]

In ClojureScript, I get:

["ab" "; " nil "ab" nil ";"]

I'm not sure to what extent this is a platform distinction and to what extent it's a bug. Returning nils and seperators from clojure.string/split's output seems like it's against string.split's contract?






[CLJS-1315] Warning on Google Closure enum property access with / Created: 18/Jun/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Edge case in / usage, EventType/CLICK does not trigger a warning. Foo/bar always means that Foo is a namespace, it cannot be used for the static field access pattern common in Java as there's no reflection information in JavaScript to determine this.






[CLJS-1294] Macroexpand only accept quoted lists Created: 01/Jun/15  Updated: 12/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Julien Eluard Assignee: Julien Eluard
Resolution: Unresolved Votes: 0
Labels: newbie

Attachments: Text File CLJS-1294.patch    

 Description   

In Clojure macroexpand and macroexpand-1 accept any quoted argument while in ClojureScript anything but quoted seq will throw an exception.



 Comments   
Comment by Julien Eluard [ 01/Jun/15 2:16 PM ]

In Clojure some special forms are handled specifically i.e. (macroexpand '(Boolean true)) => (new Boolean true).

I am not sure if/how it applies to ClojureScript.

Comment by David Nolen [ 14/Jul/15 5:58 AM ]

This patch needs to be rebased to master. Thanks!





[CLJS-1286] REPL environment should be able to provide advice if source mapping fails Created: 23/May/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

For example browser REPL will often need users to supply :host-port, :host, and :asset-path in order to correctly parse files from stacktraces.






[CLJS-1278] Asserts still fail while :require-ing .js file (either in :libs or in :source-paths) (same as CLJS-1196) Created: 20/May/15  Updated: 14/Jul/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3269
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Michal Till Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File cljs_1278.patch    

 Description   

Following on CLJS-1196, I can't get it to work.

In version 0.0-3264 lein-cljsbuild crashed on weird eception `Caused by: java.lang.IllegalArgumentException: No implementation of method: :make-reader of protocol: #'clojure.java.io/IOFactory found for class: nil"` but the current version 0.0-3269 gives the same failed assertion as previously.

I've put up a sample project to illustrate the issue.

Steps to reproduce:

`git clone https://github.com/tillda/stackone`
`cd stackone`
`git checkout 537e5c69b844bc53c159e85cafc24310543cc918`
`lein clean && lein cljsbuild once temp`

Expected behaviour: cljs compiled successfully with src/vendor/client/closure.js and env/stackone/helpersjs.js being included.

Actual behaviour:

```
Compiling "resources/public/lein-cljsbuild-temp/dev-mode-deps.js" failed.
Exception in thread "main" java.lang.AssertionError: Assert failed: (or (file? x) (url? x) (string? x)), compiling/private/var/folders/ym/l2qxd7l97kzfzftrdpqsclm40000gn/T/form-init3642888309490821030.clj:1:125)
at clojure.lang.Compiler.load(Compiler.java:7249)
at clojure.lang.Compiler.loadFile(Compiler.java:7175)
at clojure.main$load_script.invoke(main.clj:275)
at clojure.main$init_opt.invoke(main.clj:280)
at clojure.main$initialize.invoke(main.clj:308)
at clojure.main$null_opt.invoke(main.clj:343)
at clojure.main$main.doInvoke(main.clj:421)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.AssertionError: Assert failed: (or (file? x) (url? x) (string? x))
at cljs.util$ext.invoke(util.cljc:115)
at cljs.closure$source_on_disk.invoke(closure.clj:1206)
at cljs.closure$output_unoptimized$fn__3708.invoke(closure.clj:1235)
at clojure.core$map$fn__4551.invoke(core.clj:2622)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:507)
at clojure.core$seq__4126.invoke(core.clj:135)
at clojure.core$filter$fn__4578.invoke(core.clj:2677)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:507)
at clojure.core$seq__4126.invoke(core.clj:135)
at clojure.core$map$fn__4551.invoke(core.clj:2614)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.Cons.next(Cons.java:39)
at clojure.lang.RT.next(RT.java:674)
at clojure.core$next__4110.invoke(core.clj:64)
at clojure.core$str$fn__4186.invoke(core.clj:528)
at clojure.core$str.doInvoke(core.clj:526)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invoke(core.clj:628)
at cljs.closure$deps_file.invoke(closure.clj:1040)
at cljs.closure$output_deps_file.invoke(closure.clj:1060)
at cljs.closure$output_unoptimized.doInvoke(closure.clj:1243)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invoke(core.clj:630)
at cljs.closure$build.invoke(closure.clj:1514)
at cljs.closure$build.invoke(closure.clj:1426)
at cljsbuild.compiler$compile_cljs$fn__3884.invoke(compiler.clj:81)
at cljsbuild.compiler$compile_cljs.invoke(compiler.clj:80)
at cljsbuild.compiler$run_compiler.invoke(compiler.clj:187)
at user$eval4018$iter_40544058$fn4059$fn_4077.invoke(form-init3642888309490821030.clj:1)
at user$eval4018$iter_40544058$fn_4059.invoke(form-init3642888309490821030.clj:1)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
at clojure.lang.LazySeq.seq(LazySeq.java:49)
at clojure.lang.RT.seq(RT.java:507)
at clojure.core$seq__4126.invoke(core.clj:135)
at clojure.core$dorun.invoke(core.clj:3007)
at clojure.core$doall.invoke(core.clj:3023)
at user$eval4018.invoke(form-init3642888309490821030.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6792)
at clojure.lang.Compiler.eval(Compiler.java:6782)
at clojure.lang.Compiler.load(Compiler.java:7237)
... 11 more
Subprocess failed
```



 Comments   
Comment by David Nolen [ 20/May/15 10:21 AM ]

This issue is in danger of being closed. Please supply minimal steps to reproduce that do not involve anything other than the ClojureScript compiler. We no longer have time to wade through the indirection introduced by cljsbuild or any other downstream tooling. Thanks.

Comment by Michal Till [ 20/May/15 11:14 AM ]

@David Nolen: I have created a failing minimal testcase based on the Quick Start document. Here it is: https://github.com/tillda/cljs-testcase/

Comment by David Nolen [ 20/May/15 11:27 AM ]

Michal the failing example is not correct. You are not supplying any :libs option.

Comment by Michal Till [ 20/May/15 11:45 AM ]

Ah! Thank you very much! This additional issue was therefore my error. Now it seems to work even in my "big" example.

However it would be cool if there was a meaningful error message stating that a file path can't be resolved. If one is not an expert in the cljs compiler this is almost impossible to figure out. After all the error message in the CLJS-1196 issue and in this wrongfully reported one are exactly the same.

You may close this issue.

Comment by David Nolen [ 20/May/15 11:55 AM ]

We'll leave it open for the improving the error message.

Comment by Sebastian Bensusan [ 22/May/15 7:16 AM ]

Added the check in cljs.closure/source-on-disk where there is info for the error message.

For the supplied case, the error message is:

java.lang.IllegalArgumentException: The file file:/home/carlos/Playground/cljs-testcase/src/hello_world/closure.js 
lacks an associated source file. If it is a JavaScript library please add it to :libs}}

If a different wording or location of the check is needed, I'll submit a new patch with corrections.

Notes:

  • Changed:(:provides js) to (-provides js) in order to be consistent with IJavaScript.
  • cljs.clojure/source-on-disk takes a js argument that should satisfy with IJavaScript and ISourceMap if :source-map is enabled but the implementation is hardcoded to maps because :source-map and :source-url are used instead of ISourceMap methods -source-map and -source-url. I propose to extend PersistentMap and PersistentArrayMap to ISourceMap to make source-on-disk compliant with both protocols.




[CLJS-1274] Allow assignment to namespace-qualified names in current namespace Created: 17/May/15  Updated: 17/May/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3211
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: lvh Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

In Clojure, you can def to a namespace-qualified name as long as it's in the current namespace. You can't do that in Clojurescript. This makes writing some macros that def to a local name a bit more annoying, since the syntax-quote will automatically namespace-qualify any symbols, so you have to write the somewhat unsightly and not super obvious ~'sym.






[CLJS-1271] Missing warning when assigning namespaces via def Created: 17/May/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Sebastian Bensusan Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Currently you can assign a Closure namespace to a var without getting a warning.

Minimal sample:

(ns import-names.core
  (:import [goog debug]))

(def debug goog.debug)


 Comments   
Comment by David Nolen [ 29/May/15 12:30 PM ]

The example case is a bit complected. Besides importing a name that matches a def you are also assigning a google closure namespace to a local. This will likely cause problems on its own. We need more information.

Comment by Sebastian Bensusan [ 29/May/15 12:46 PM ]

We should check that :require ed and :import ed namespaces are not used as values and then warn about it.





[CLJS-1266] Node: Rename .cljs to .cljc -> old filenames in stacktrace Created: 12/May/15  Updated: 12/May/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3269
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Using QuickStart, set up Node REPL.

Manually add a foo/bar.cljs to filesystem with

(ns foo.bar)

(defn throw-ex [] (ffirst 1))

(defn call-me [] (throw-ex))

Check that it works:

cljs.user=> (require 'foo.bar)
nil
cljs.user=> (foo.bar/call-me)
repl:13
throw e__4210__auto__;
      ^
Error: 1 is not ISeqable
    at Object.cljs$core$seq [as seq] (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:956:20)
    at Object.cljs$core$first [as first] (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:965:16)
    at cljs$core$ffirst (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:1398:11)
    at foo$bar$throw_ex (/Users/mfikes/Desktop/hello_world-node/out/foo/bar.cljs:3:20)
    at foo$bar$call_me (/Users/mfikes/Desktop/hello_world-node/out/foo/bar.cljs:5:19)
    at repl:1:105
    at repl:9:3
    at repl:14:4
    at Object.exports.runInThisContext (vm.js:74:17)
    at Domain.<anonymous> ([stdin]:41:34)

Then manually move bar.cljs to bar.cljc and add a new symbol so it looks like:

(ns foo.bar)

(defn throw-ex [] (ffirst 1))

(defn call-me [] (throw-ex))

(defn call-again [] (call-me))

Then reload the ns and use the new symbol:

cljs.user=> (require 'foo.bar :reload)
nil
cljs.user=> (foo.bar/call-again)
repl:13
throw e__4210__auto__;
      ^
Error: 1 is not ISeqable
    at Object.cljs$core$seq [as seq] (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:956:20)
    at Object.cljs$core$first [as first] (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:965:16)
    at cljs$core$ffirst (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:1398:11)
    at foo$bar$throw_ex (/Users/mfikes/Desktop/hello_world-node/out/foo/bar.cljs:3:20)
    at foo$bar$call_me (/Users/mfikes/Desktop/hello_world-node/out/foo/bar.cljs:5:19)
    at foo$bar$call_again (/Users/mfikes/Desktop/hello_world-node/out/foo/bar.cljs:5:19)
    at repl:1:108
    at repl:9:3
    at repl:14:4
    at Object.exports.runInThisContext (vm.js:74:17)

This illustrates the defect. call_again and the other symbols are shown as being in the old filename.

Stop the REPL and restart it to see correct behavior:

cljs.user=> :cljs/quit
orion:hello_world-node mfikes$ rlwrap java -cp cljs.jar:src clojure.main node_repl.clj
Reading analysis cache for jar:file:/Users/mfikes/Desktop/hello_world-node/cljs.jar!/cljs/core.cljs
Compiling src/foo/bar.cljc
ClojureScript Node.js REPL server listening on 49397
Watch compilation log available at: out/watch.log
To quit, type: :cljs/quit
cljs.user=> (require 'foo.bar)
nil
cljs.user=> (foo.bar/call-again)
repl:13
throw e__4210__auto__;
      ^
Error: 1 is not ISeqable
    at Object.cljs$core$seq [as seq] (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:956:20)
    at Object.cljs$core$first [as first] (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:965:16)
    at cljs$core$ffirst (/Users/mfikes/Desktop/hello_world-node/out/cljs/core.cljs:1398:11)
    at foo$bar$throw_ex (/Users/mfikes/Desktop/hello_world-node/out/foo/bar.cljc:3:20)
    at foo$bar$call_me (/Users/mfikes/Desktop/hello_world-node/out/foo/bar.cljc:5:19)
    at foo$bar$call_again (/Users/mfikes/Desktop/hello_world-node/out/foo/bar.cljc:7:22)
    at repl:1:108
    at repl:9:3
    at repl:14:4
    at Object.exports.runInThisContext (vm.js:74:17)


 Comments   
Comment by Mike Fikes [ 12/May/15 2:04 PM ]

FWIW as a comparison, the same use case works properly with Clojure 1.7.0-beta2.





[CLJS-1259] Incorrect warnings on type hinted maths Created: 09/May/15  Updated: 14/Jul/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3255
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Erik Ouchterlony Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug, math, typehints


 Description   

Variables type hinted as int or double are not recognized as numbers, e.g.

(def ^int i 1)
(+ i i)
WARNING: cljs.core/+, all arguments must be numbers, got [int int] instead. at line 1 <cljs repl>
2


 Comments   
Comment by David Nolen [ 14/Jul/15 6:07 AM ]

The real issue is that there is no support for numeric type hints.





[CLJS-1255] cljs.test file-and-line detection is not useful in browser testing Created: 07/May/15  Updated: 14/Jul/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3211
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Stephen Nelson Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Chrome



 Description   

cljs.test reports using do-report, which adds file and line information computed from javascript stack traces. In chrome at least, these stack traces are not useful:

"Error
    at http://localhost:3449/js/cljs/test.js:261:69
    at cljs$test$do_report (http://localhost:3449/js/cljs/test.js:268:3)
    at http://localhost:3449/js/test/test_tests.js:491:21
    at test.test_tests.test_has_fails.cljs$lang$test (http://localhost:3449/js/test/test_tests.js:502:4)
    at http://localhost:3449/js/cljs/test.js:384:42
    at http://localhost:3449/js/cljs/test.js:387:4
    at cljs$test$run_block (http://localhost:3449/js/cljs/test.js:320:13)
    ..."

The `file-and-line` stack trace parser doesn't parse this correctly, resulting in a message like this:

FAIL in (test-function) (at http:384:42)

Note the lack of a useful file/namespace reference, and that the line number refers to the compiled javascript rather than the source clojurescript.



 Comments   
Comment by Stephen Nelson [ 07/May/15 9:15 PM ]

Prior to the release of cljs.test my company maintained an internal port of clojure.test that did better reporting than cljs.test's by adding source metadata from &form to the do-report calls generated by assert-expr. This approach was great for internal use but might not be suitable for cljs.test as it could reduce portability of assert-expr between clojure and clojurescript. Another approach could be dynamically bind source metadata in the body generated by try-expr. I'd be willing to implement and contribute code if you can provide some indication of your preferred approach.

Our version of assert-expr also injected a 'reporter function', {{(function(a,b,c){a.apply(b.c)})}}, which we would invoke from report, e.g. (reporter (.-debug js/console) js/console args). This causes the clickable link on the right hand side of chrome's console output to link to the source map location of the test expression, rather than the report function.

Comment by David Nolen [ 14/Jul/15 6:09 AM ]

The correct thing to do here is to move the browser REPL stacktrace parsing into a shared library i.e. .cljc that can be loaded into either environment to handle browser difference.





[CLJS-1245] Implement bound-fn Created: 03/May/15  Updated: 05/May/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Brandon Bloom Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

See discussion on http://dev.clojure.org/jira/browse/CLJS-210






[CLJS-1242] Add cljs.core/pattern? predicate Created: 03/May/15  Updated: 27/Jul/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Brandon Bloom Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: patch

Attachments: Text File CLJS-1242_v01.patch    
Patch: Code and Test

 Description   

Just like http://dev.clojure.org/jira/browse/CLJS-1241 , this helps with clj/cljs cross-platform development.



 Comments   
Comment by Brandon Bloom [ 03/May/15 2:38 PM ]

See also http://dev.clojure.org/jira/browse/CLJ-1720

Comment by Brandon Bloom [ 10/May/15 11:36 PM ]

Note that there already is a cljs.core/regexp?, since it's used in a few places by core.

Comment by Samuel Miller [ 27/Jul/15 9:27 PM ]

The patch needs to be updated because of the folder changes

Tested on Linux with V8, SpiderMonkey, and Nashorn. Repljs also functions correctly.

You could have the `pattern?` function point to the `regexp?` function. Maybe there is some reason to keep separate (ability to have non-regex patterns?)





[CLJS-1241] Add cljs.core/boolean? predicate Created: 03/May/15  Updated: 31/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Brandon Bloom Assignee: Unassigned
Resolution: Unresolved Votes: 2
Labels: patch

Attachments: Text File CLJS-1241_v01.patch    
Patch: Code and Test

 Description   

I'm constantly re-implementing this predicate...

It's also important for clj/cljs compatibility.



 Comments   
Comment by Brandon Bloom [ 03/May/15 2:32 PM ]

See also: http://dev.clojure.org/jira/browse/CLJ-1719

Comment by Mike Fikes [ 03/May/15 2:52 PM ]

I tried this patch and it works correctly for me.

The docstring uses "typeof". Perhaps "type of" was intended?

cljs.user=> (doc boolean?)
-------------------------
cljs.core/boolean?
([x])
  Returns true if the typeof x is boolean, false otherwise.

nil
Comment by Brandon Bloom [ 03/May/15 7:41 PM ]

I did mean "typeof", which is a javascript-ism. A better doc string may be less javascript-specific...

Comment by Mike Fikes [ 31/Jan/16 3:17 PM ]

Patch CLJS-1241_v01.patch no longer applies on master.

Comment by Brandon Bloom [ 31/Jan/16 7:13 PM ]

Not going to bother updating patch until there is movement on http://dev.clojure.org/jira/browse/CLJ-1719





[CLJS-1238] Setting *main-cli-fn* when using :target :nodejs shouldn't be manditory Created: 01/May/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: Jeremy Shoemaker Assignee: Unassigned
Resolution: Unresolved Votes: 2
Labels: None

Attachments: Text File nodejs-main-cli-fn.patch    
Patch: Code

 Description   

Currently, when you use :target :nodejs in the build options for ClojureScript, the resulting code requires you to set *main-cli-fn* to a function.

This prevents someone from writing a library that can be used by JavaScript developers because it forces code execution on require. It also makes writing a CLI tool that can be distributed using NPM less straightforward. I ran into this issue trying to create a Leiningen template for writing CLI tools that could be installed using npm install or npm link. I had a wrapper script to take care of the CLI use-case, and intended to write the ClojureScript module in a more library oriented way, but ran into issues. I could work around this by not using the wrapper script, but it got me thinking about the more general library issue.

I don't see any reason why you should be forced to set *main-cli-fn* and so I'm suggesting making it optional.

Attached is a patch that makes it optional but retains the check for whether the value it is set to is a function in the case where it is set.

This is my first time submitting a change to a project using a git patch and not a pull request, so let me know if I've made the patch wrong.



 Comments   
Comment by Jeremy Shoemaker [ 01/May/15 7:27 PM ]

I just noticed the priority defaulted to "Major". I don't know if I'd say it's major, so feel free to bump it down if that doesn't seem appropriate.

Comment by Ning Sun [ 18/Feb/16 4:08 AM ]

+1.

I was working on a clojurescript library and going to build it as a node library. Currently blocked by this.

Comment by Mike Fikes [ 20/Feb/16 8:07 AM ]

Patch no longer applies.





[CLJS-1237] ns-unmap doesn't work on refers from cljs.core Created: 01/May/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Chouser Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: ns-unmap

Attachments: Text File 0001-CLJS-1237-ns-unmap-adds-to-namespace-s-excludes.patch     Text File 0002-CLJS-1237-ns-unmap-adds-to-namespace-s-excludes.patch    
Patch: Code

 Description   

In ClojureScript, using ns-unmap on a symbol from cljs.core doesn't exclude it from the current namespace. Note that both a function and a macro still exist, even after unmapping:

To quit, type: :cljs/quit  
cljs.user=> (ns-unmap 'cljs.user 'when) ;; macro
true  
cljs.user=> (ns-unmap 'cljs.user 'not)  ;; function
true  
cljs.user=> (when 1 2)  
2  
cljs.user=> (not false)  
true  

This differs from the behavior of Clojure's ns-unmap. Note the appropriate errors when attempting to use unmapped symbols:

Clojure 1.7.0-beta1
user=> (ns-unmap 'user 'when) ;; macro
nil
user=> (ns-unmap 'user 'not)  ;; function
nil
user=> (when 1 2)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: when in this context, compiling:(NO_SOURCE_PATH:11:1) 
user=> (not false)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: not in this context, compiling:(NO_SOURCE_PATH:12:1) 

Somehow ClojureScript's ns-unmap needs to add the symbol to the current namespace's :excludes set. Note that the def special form does this already (after it displays a warning).

We have two solutions. 0001 extends the ns form's :merge behavior to support :excludes, and then uses this in ns-unmap. If the enhancement to ns isn't wanted, patch 0002 changes ns-unmap to update :excludes directly.



 Comments   
Comment by David Nolen [ 05/May/15 7:23 AM ]

The second patch is preferred. However it seems the second patch is too permissive. The :excludes logic should only be applied if the symbol identifies a core macro or fn.

Comment by Chouser [ 05/May/15 3:46 PM ]

The ns form's own :refer-clojure :exclude accepts arbitrary symbols and adds them to the namespace's :excludes set, which seems like the same permissiveness problem. Do you want a patch that addresses the permissiveness of both ns and ns-unmap in this ticket, or should such a patch go in a new ticket?

Comment by David Nolen [ 05/May/15 4:08 PM ]

New ticket to fix the bug that :exclude doesn't check the symbol list for cljs.core declared vars, and an updated patch here please.





[CLJS-1223] cljs.repl/doc for unqualified .. macro Created: 24/Apr/15  Updated: 24/Apr/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3126
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Note: Also affects 0.0-3211 (not yet available in JIRA pulldown)
Quick Start cljs.jar
OS X



 Description   

In the REPL, (doc ..) fails, while it succeeds for similar macros like (doc ->), and it also succeeds for ns-qualified: (doc cljs.core/..).

$ java -jar cljs.jar -m cljs.repl.node
ClojureScript Node.js REPL server listening on 54359
To quit, type: :cljs/quit
cljs.user=> *clojurescript-version*
"0.0-3211"
cljs.user=> (doc ..)
-------------------------
/.
  nil

nil
cljs.user=> (doc cljs.core/..)
-------------------------
cljs.core/..
([x form] [x form & more])
Macro
  form => fieldName-symbol or (instanceMethodName-symbol args*)

  Expands into a member access (.) of the first member on the first
  argument, followed by the next member on the result, etc. For
  instance:

  (.. System (getProperties) (get "os.name"))

  expands to:

  (. (. System (getProperties)) (get "os.name"))

  but is easier to write, read, and understand.

nil





[CLJS-1222] Sequence of a stateful transducer is producing the wrong answer Created: 24/Apr/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Lucas Cavalcanti Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug, cljs, collections
Environment:

OSX 10.10.3, java 1.8.0-ea-b124



 Description   

I'm producing more than one element on the 1-arity of the transducer, and sequence is only considering the last one.

Here is the transducer and the tests that fail for sequence:

(defn sliding-window [n]
  (fn [rf]
    (let [a #js []]
      (fn
        ([] (rf))
        ([result]
         (loop [] ;; Here I'm emitting more than one element
           (when (not-empty a)
             (rf result (vec (js->clj a)))
             (.shift a)
             (recur))))
        ([result input]
         (.push a input)
         (if (== n (.-length a))
           (let [v (vec (js->clj a))]
             (.shift a)
             (rf result v))
           result))))))

;;This test fails! =(
(deftest sliding-window-in-a-sequence
  (is (= [[5 4 3]
          [4 3 2]
          [3 2 1]
          [2 1]
          [1]]
         (sequence (sliding-window 3) [5 4 3 2 1])))

  (is (= [[2 1]
          [1]]
         (sequence (sliding-window 3) [2 1]))))


 Comments   
Comment by Lucas Cavalcanti [ 24/Apr/15 11:18 AM ]

I could make it work by recurring on the result.

([result]
  (loop [res result]
    (if (not-empty a)
      (let [v (vec (js->clj a))]
        (.shift a)
        (recur (rf res v)))
      res)))

even so it's weird that the previous version behaves differently on core.async and sequences in cljs and clj

Comment by David Nolen [ 26/Apr/15 4:04 AM ]

Please demonstrate the problem without core.async. Thanks.

Comment by Lucas Cavalcanti [ 26/Apr/15 7:32 PM ]

Hi,

the last test I posted on the ticket, fails in cljs, but not in clj:

;;This test fails! =(
(deftest sliding-window-in-a-sequence
  (is (= [[5 4 3]
          [4 3 2]
          [3 2 1]
          [2 1]
          [1]]
         (sequence (sliding-window 3) [5 4 3 2 1])))

  (is (= [[2 1]
          [1]]
         (sequence (sliding-window 3) [2 1]))))
Comment by David Nolen [ 27/Apr/15 7:43 AM ]

I've removed the core.async bits from the description to clarify the issue.

Comment by David Nolen [ 10/May/15 2:40 PM ]

The implementation of sliding-window above does not appear to be correct, it doesn't return the result. This ticket needs more information.

Comment by Lucas Cavalcanti [ 10/May/15 3:51 PM ]

As I said on http://dev.clojure.org/jira/browse/CLJS-1222?focusedCommentId=38620&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-38620

changing the 1-arity of the sliding-window to that fixes the transducer.

The point of this ticket now is that the behavior of the same (wrong) transducer in clj (both core.async and sequence) and cljs (core.async) is different than cljs sequence.





[CLJS-1211] Automatically requiring :main namespace under :none fails in IE9 Created: 17/Apr/15  Updated: 17/Apr/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3058, 0.0-3115, 0.0-3196, 0.0-3117, 0.0-3119, 0.0-3123, 0.0-3126
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Immo Heikkinen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Internet Explorer 9



 Description   

Automatic goog/base.js inclusion using :none & :main doesn't work in Internet Explorer 9. The following error message is printed to the console:

ClojureScript could not load :main, did you forget to specify :asset-path?

There seems to be a timing issue after writing script tags to the HTML document. Inline JavaScript for requiring the main namespace gets executed while goog is still undefined.

I played a bit with this but couldn't get it working in IE9. I tried moving the require statement to a separate JS file and adding a script tag to load that file, then goog was no longer undefined but I still got an error message:

SCRIPT5022: Undefined nameToPath for goog.string
base.js, line 753 character 9

The feature seems to work fine in other browsers (also IE10). Probably not worth fixing but at least the limitation is documented now in case someone else wonders the why it doesn't work in IE9.






[CLJS-1207] Emit a warning if multiple resources found for a ClojureScript namespace Created: 15/Apr/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

We should emit a simple warning if a namespace doesn't not appear to be unique on the classpath.






[CLJS-1195] generic reusable command line argument parsing for REPLs Created: 10/Apr/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: newbie


 Description   

REPLs are more or less started in the same way and all the builtin ones provide a -main entry point. We should supply reusable command line argument parsing that any REPL can use to get standard command line driven start.






[CLJS-1186] add :postamble option to compiler Created: 02/Apr/15  Updated: 12/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Michael Bradley Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: cljs

Attachments: Text File cljs_1186.patch    

 Description   

Similar to CLJS-723:

1) :postamble's value will be a vector of paths
2) the compiled output is appended with the contents of the files at those paths
3) the generated source map points to the correct/adjusted line numbers



 Comments   
Comment by David Nolen [ 12/Feb/16 4:42 PM ]

Would like to hear more use cases for this one.





[CLJS-1174] Simple warning if a namespace with dashes not found but a file path with dashes exists Created: 27/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: easy





[CLJS-1164] quot and rem are inefficient Created: 24/Mar/15  Updated: 20/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Francis Avila Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: math

Attachments: Text File CLJS-1164-1.patch     Text File cljs-1164.patch    
Patch: Code and Test

 Description   

The implementation of the quot and rem functions are needlessly complicated. Currently they are:

(defn quot [n d] (fix (/ (- n (js-mod n d)) d)))
(defn rem [n d] (- n (* d (quot n d))))

However all numbers in js are doubles already, so all this is unnecessary:

(defn quot [n d] (fix (/ n d)))
(defn rem [n d] (js-mod n d)))

Notice that "rem" is simply js-mod, and I'm not sure why no one noticed this before. I keep js-mod for now since a lot of code uses it, and if cljs ever grows a number tower the distinction may be important.

Patch attached, which also:

  • Creates a macro version of quot and rem.
  • Updates documentation for quot, rem, js-mod and mod for clarity.
  • Implement fix (private function to round to zero) with ES6 Math.trunc() if available.

Existing quot and rem tests pass, although there could be some better tests of edge cases (negative decimal num or div, NaN and +-Infinity args).



 Comments   
Comment by Francis Avila [ 24/Mar/15 12:27 PM ]

Better tests found rounding errors in my updated rem, which should stay as-is. (Not simply js-mod after all! Seems to round args first? Not obvious from the spec.) Changed quot however is correct and introduces less error than the current one. Will update patch and tests when I get a chance.

Comment by Francis Avila [ 29/Mar/15 12:39 AM ]

Working patch with tests attached. Tests expanded to cover floating-point cases. rem is now fundamentally the same as master (was more accurate than js-mod!!), but returns results consistent with js-mod for non-finite args or zero divisor.

Comment by Mike Fikes [ 31/Jan/16 3:23 PM ]

cljs-1164.patch no longer applies on master

Comment by Andrea Richiardi [ 14/Feb/16 9:02 PM ]

Patch now applies. I only tested with Nashorn:

V8_HOME not set, skipping V8 tests
SPIDERMONKEY_HOME not set, skipping SpiderMonkey tests
JSC_HOME not set, skipping JavaScriptCore tests
Testing with Nashorn

...

Ran 185 tests containing 17195 assertions.
0 failures, 0 errors.
Tested with 1 out of 4 possible js targets
Comment by Andrea Richiardi [ 14/Feb/16 9:02 PM ]

Patch cleaned up

Comment by Mike Fikes [ 20/Feb/16 10:11 PM ]

Successfully ran Andrea's update to Francis's patch through V8, SpiderMonkey, JavaScriptCore, and Nashorn unit tests.

I also manually ran some of the unit tests in bootstrapped ClojureScript built with the patch.

LGTM.

Comment by Mike Fikes [ 20/Feb/16 10:23 PM ]

Since this is a low-level numerics update, also ran the unit tests through ChackraCore (successfully).





[CLJS-1160] Source map js / cljs line number mixup Created: 23/Mar/15  Updated: 23/Mar/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3126
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

QuickStart Browser REPL Chrome OS X



 Description   

Note the source location for cljs.core.LazySeq.sval in the following 0.0-3126 stacktrace:

ClojureScript:cljs.user> (map ffirst (range))
Error: 0 is not ISeqable
	 cljs$core$seq (out/cljs/core.cljs:951:13)
	 cljs$core$first (out/cljs/core.cljs:960:7)
	 cljs$core$ffirst (out/cljs/core.cljs:1393:3)
	 cljs.core.map.cljs$core$map__2 (out/cljs/core.cljs:4046:30)
	 cljs.core.LazySeq.sval (out/cljs/core.cljs:11400:3)
	 cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (out/cljs/core.cljs:2884:12)
	 cljs$core$seq (out/cljs/core.cljs:938:7)
	 cljs$core$pr_sequential_writer (out/cljs/core.cljs:8426:13)

It turns out that line 11400 is actually in core.js, but the source map logic is evidently getting tripped up by this one, and core.cljs gets reported with the JS line numbers (core.cljs doesn't even have that many lines).

Note that this also occurs on master (where CLJS-1154 and CLJS-1157 have landed):

ClojureScript:cljs.user> (map ffirst (range))
Error: 0 is not ISeqable
	 cljs.core/seq (out/cljs/core.cljs:951:20)
	 cljs.core/first (out/cljs/core.cljs:960:16)
	 cljs$core$ffirst (out/cljs/core.cljs:1393:11)
	 cljs.core.map.cljs$core$map__2 (out/cljs/core.cljs:4046:30)
	 cljs.core.LazySeq.sval (out/cljs/core.cljs:11400:3)
	 cljs.core.LazySeq.cljs$core$ISeqable$_seq$arity$1 (out/cljs/core.cljs:2884:12)
	 cljs.core/seq (out/cljs/core.cljs:938:25)
	 cljs$core$pr_sequential_writer (out/cljs/core.cljs:8426:20)





[CLJS-1159] compiled files with warnings that otherwise don't need recompilation will not emit warnings on the next compile Created: 23/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None


 Description   

The aggressive caching approach is odds with warning visibility. It probably makes sense for a compiled file with warnings to always return true for requires-compilation?.






[CLJS-1153] Typed Array backed PersistentVector based on clojure.core/Vec Created: 19/Mar/15  Updated: 05/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Adrian Medina Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: enhancement

Attachments: Text File 1153.patch    
Patch: Code

 Description   

Goal is to add support for vectors based on clojure.core/Vec, built on top of JavaScript Typed Arrays.

My hope is that this would allow for both efficient creation of vectors from existing Typed Arrays without intermediate conversion to normal JavaScript arrays, as well as efficient concatenation of the composite arrays of the vector back into a Typed Array when necessary via an enhanced cljs.core/into-array.

Implementation is based heavily on clojure/core/gvec.clj, cljs.core/PersistentVector, and cljs.core/TransientVector.

Performance should be comparable to cljs.core/PersistentVector, although there is additional constant overhead with TypedArray instantiation compared to js/Array.

Adds cljs.core/Vec, cljs.core/TransientVec, cljs.core/vector-of, and updates cljs.core/into-array.



 Comments   
Comment by Adrian Medina [ 19/Mar/15 8:39 PM ]

I still have to test, I will update the issue when that is complete. I just wanted to get my first patch up for review as quickly as possible.

Comment by Francis Avila [ 19/Mar/15 11:59 PM ]

No mention of Uint8ClampedArray.

Should Vec type- or range-check assignments? In Clojure these fail (even with unchecked-math):

  • (vector-of :byte 128) returns [-128]
  • (vector-of :byte "1") returns [1]
  • (vector-of :byte (js-obj)) returns [0]

If we're going to expose host primitive arrays via cljs apis, should we also bring the various other array functions in line with Clojure (and ClojureCLR, which also has extra uint, ubyte, etc types) like you are doing with into-array? Some or all of these issues may warrant another ticket instead, or maybe even a design page:

  • make-array ignores type argument and lacks higher dimensions.
  • object-array, int-array, etc. maybe should return TypedArrays.
  • Missing ubyte-array, ushort-array, uint-array (like ClojureCLR)
  • Missing aset-* setters. (Meaningless in js unless we range-check.)
  • aclone and amap preserve type of input array in Clojure, but not in cljs.
  • missing array casters: bytes, shorts, chars, ints, etc.
  • While we're at it, primitive coercion functions (e.g. int, long, unchecked-int, etc) are either no-ops or differ from clojure. (e.g., int in cljs is like unchecked-int in clojure, but unchecked-int in cljs does nothing). Maybe these should be dropped or should match the javascript ToInt32, ToInt16, etc abstract operations (i.e. those used when assigning to TypedArrays). Maybe these match java semantics also?
Comment by Mike Fikes [ 05/Feb/16 8:18 PM ]

Patch 1153.patch no longer applies





[CLJS-1151] Noisy errors when referring to symbol in undefined ns Created: 19/Mar/15  Updated: 22/Mar/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3126
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Quick Start / OS X / Chrome



 Description   

Run through the Quick Start to the point where you have the browser REPL running.

If you refer to a symbol in an undefined ns you will get really noisy errors:

ClojureScript:cljs.user> foo.bar/a
WARNING: Use of undeclared Var foo.bar/a at line 1 <cljs repl>
ReferenceError: foo is not defined
ReferenceError: foo is not defined
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:1:89)
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:9:3)
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:14:4)
    at http://localhost:9000/out/clojure/browser/repl.js:42:267
    at clojure$browser$repl$evaluate_javascript (http://localhost:9000/out/clojure/browser/repl.js:45:4)
    at Object.callback (http://localhost:9000/out/clojure/browser/repl.js:242:169)
    at goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)
    at goog.net.xpc.CrossPageChannel.xpcDeliver (http://localhost:9000/out/goog/net/xpc/crosspagechannel.js:733:12)
    at Function.goog.net.xpc.NativeMessagingTransport.messageReceived_ (http://localhost:9000/out/goog/net/xpc/nativemessagingtransport.js:321:13)
    at Object.goog.events.fireListener (http://localhost:9000/out/goog/events/events.js:741:21)

Now, define the namespace, but don't actually define the symbol. For example:

(ns foo.bar)

(def z 3)

Now, require the namespace and do the same. The noise will go away.

ClojureScript:cljs.user> (require 'foo.bar)
nil
ClojureScript:cljs.user> foo.bar/a
WARNING: Use of undeclared Var foo.bar/a at line 1 <cljs repl>
nil

Additional info: Now if you attempt to refer to other unknown symbols, you will get different kinds of "noise" depending on whether the ns simply starts with foo:

ClojureScript:cljs.user> foo.baz/a
WARNING: No such namespace: foo.baz, could not locate foo/baz.cljs at line 1 <cljs repl>
WARNING: Use of undeclared Var foo.baz/a at line 1 <cljs repl>
TypeError: Cannot read property 'a' of undefined
TypeError: Cannot read property 'a' of undefined
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:1:96)
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:9:3)
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:14:4)
    at http://localhost:9000/out/clojure/browser/repl.js:42:267
    at clojure$browser$repl$evaluate_javascript (http://localhost:9000/out/clojure/browser/repl.js:45:4)
    at Object.callback (http://localhost:9000/out/clojure/browser/repl.js:242:169)
    at goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)
    at goog.net.xpc.CrossPageChannel.xpcDeliver (http://localhost:9000/out/goog/net/xpc/crosspagechannel.js:733:12)
    at Function.goog.net.xpc.NativeMessagingTransport.messageReceived_ (http://localhost:9000/out/goog/net/xpc/nativemessagingtransport.js:321:13)
    at Object.goog.events.fireListener (http://localhost:9000/out/goog/events/events.js:741:21)
ClojureScript:cljs.user> goo.baz/a
WARNING: No such namespace: goo.baz, could not locate goo/baz.cljs at line 1 <cljs repl>
WARNING: Use of undeclared Var goo.baz/a at line 1 <cljs repl>
ReferenceError: goo is not defined
ReferenceError: goo is not defined
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:1:89)
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:9:3)
    at eval (eval at <anonymous> (http://localhost:9000/out/clojure/browser/repl.js:42:272), <anonymous>:14:4)
    at http://localhost:9000/out/clojure/browser/repl.js:42:267
    at clojure$browser$repl$evaluate_javascript (http://localhost:9000/out/clojure/browser/repl.js:45:4)
    at Object.callback (http://localhost:9000/out/clojure/browser/repl.js:242:169)
    at goog.messaging.AbstractChannel.deliver (http://localhost:9000/out/goog/messaging/abstractchannel.js:142:13)
    at goog.net.xpc.CrossPageChannel.xpcDeliver (http://localhost:9000/out/goog/net/xpc/crosspagechannel.js:733:12)
    at Function.goog.net.xpc.NativeMessagingTransport.messageReceived_ (http://localhost:9000/out/goog/net/xpc/nativemessagingtransport.js:321:13)
    at Object.goog.events.fireListener (http://localhost:9000/out/goog/events/events.js:741:21)


 Comments   
Comment by Mike Fikes [ 19/Mar/15 3:47 PM ]

Clarification on the description: When defining the foo.bar ns, I actually created a source file src/foo/bar.cljs containing the ns declaration and the def z.

Comment by David Nolen [ 22/Mar/15 9:49 AM ]

I'm not sure what we can do about this or that suppressing the errors from the JavaScript evaluation environment is a good idea. Property access on something that doesn't exist is what causes the JS environment to emit the error. In the later cases the property access is on something that does exist but doesn't have the property so no error.





[CLJS-1139] Repeated applications of `ns` form at the REPL are not additive Created: 17/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Michael Griffiths Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Quick start guide with Node REPL



 Description   

In a Clojure REPL, it is possible to declare the same namespace again, without existing namespaces aliases being altered or removed:

user=> (ns my-test-ns.core (:require [clojure.string :as string]))
nil
my-test-ns.core=> (def a string/blank?)
#'my-test-ns.core/a
my-test-ns.core=> (ns my-test-ns.core)
nil
my-test-ns.core=> (def a string/blank?)
#'my-test-ns.core/a
my-test-ns.core=>

ClojureScript REPLs do not behave in the same way:

ClojureScript:cljs.user> (ns my-test-ns.core (:require [clojure.string :as string]))
true
ClojureScript:my-test-ns.core> (def a string/blank?)
#<function clojure$string$blank_QMARK_(s){
return goog.string.isEmptySafe(s);
}>
ClojureScript:my-test-ns.core> (ns my-test-ns.core)
nil
ClojureScript:my-test-ns.core> (def a string/blank?)
WARNING: No such namespace: string, could not locate string.cljs at line 1 <cljs repl>
WARNING: Use of undeclared Var string/blank? at line 1 <cljs repl>
repl:13
throw e__3919__auto__;
      ^
ReferenceError: string is not defined
    at repl:1:109
    at repl:9:3
    at repl:14:4
    at Object.exports.runInThisContext (vm.js:74:17)
    at Domain.<anonymous> ([stdin]:41:34)
    at Domain.run (domain.js:197:16)
    at Socket.<anonymous> ([stdin]:40:25)
    at Socket.emit (events.js:107:17)
    at readableAddChunk (_stream_readable.js:163:16)
    at Socket.Readable.push (_stream_readable.js:126:10)
ClojureScript:my-test-ns.core>





[CLJS-1136] Initial require fails to fully load added symbols Created: 17/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Quick Start Browser REPL (OS X / Safari)



 Description   

In the Quick Start, a portion runs the user through adding a symbol (a function named foo) and then requiring the namespace and using that symbol. I'm finding that require fails and that I need to add the :reload directive.

To reproduce:

  1. Run through the Quick Start up through the browser REPL section.
  2. Set src/hello_world/core.cljs so that it does not have the foo function defined.
  3. Remove the out directory: rm -rf out
  4. Start up the REPL: rlwrap java -cp cljs.jar:src clojure.main repl.clj
  5. Connect Safari by going to http://localhost:9000
  6. Show the error console in Safari. (You'll see Hello world.)
  7. Run tail -f out/watch.log
  8. Add the foo function that adds a b to src/hello_world/core.cljs and save it.
  9. Observe that watch.log reflects recompilation
  10. Do {{ (require '[hello-world.core :as hello]) }}
  11. Do {{ (hello/foo 2 3) }}

At this point you will get:
TypeError: undefined is not an object (evaluating 'hello_world.core.foo.call')

But:

ClojureScript:cljs.user> (ns-interns 'hello-world.core)
{foo #'hello-world.core/foo, conn #'hello-world.core/conn}
ClojureScript:cljs.user> (source hello/foo)
(defn foo [a b]
  (+ a b))
nil

Now, if you :reload

ClojureScript:cljs.user> (require '[hello-world.core :as hello] :reload)
nil
ClojureScript:cljs.user> (hello/foo 2 3)
5


 Comments   
Comment by Mike Fikes [ 17/Mar/15 11:30 AM ]

Prior to step 8:

ClojureScript:cljs.user> (ns-interns 'hello-world.core)
{}

Between steps 9 and 10:

ClojureScript:cljs.user> (ns-interns 'hello-world.core)
{foo #'hello-world.core/foo, conn #'hello-world.core/conn}

My guess: Watching is causing symbols to be interned, but not usable, and this is interfering with require forcing you to include :reload.

Comment by David Nolen [ 22/Mar/15 9:46 AM ]

I'm not sure that this is actually an issue, the browser has already required the namespace, it's the entry point. Thus you really do need a :reload. But the reason you see interned symbols is that the watch process shares the compilation environment with the REPL. It may be the case that with the dramatically improved REPLs the watch option becomes entirely unnecessary and counterintuitive, let's see how it goes.





[CLJS-1134] Lift protocols from cljs.closure into cljs.protocols ns Created: 17/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Task Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

This is task towards presenting a stable API to users without reaching into the implementation namespaces.






[CLJS-1133] REPL require results in warnings to be emitted twice Created: 17/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Quick Start Browser REPL with :watch off



 Description   

Run through the Quick Start and go down through to the Browser REPL portion (https://github.com/clojure/clojurescript/wiki/Quick-Start#browser-repl), but exclude the :watch option from repl.clj.

Then further down, where the new symbol is introduced

;; ADDED
(defn foo [a b]
  (+ a b))

instead cause some duplicate symbols to be introduced in order to provoke compiler warnings:

(def a 1)
(def a 1)

(defn foo [a b]
  (+ a b))
(defn foo [a b]
  (+ a b))

Then evaluate the require statement in the tutorial and observe that the warnings are emitted twice:

ClojureScript:cljs.user> (require '[hello-world.core :as hello])
WARNING: a at line 11 is being replaced at line 12 /Users/mfikes/Desktop/hello_world/src/hello_world/core.cljs
WARNING: foo at line 14 is being replaced at line 16 /Users/mfikes/Desktop/hello_world/src/hello_world/core.cljs
WARNING: a at line 11 is being replaced at line 12 /Users/mfikes/Desktop/hello_world/src/hello_world/core.cljs
WARNING: foo at line 14 is being replaced at line 16 /Users/mfikes/Desktop/hello_world/src/hello_world/core.cljs
nil





[CLJS-1129] :modules tutorial for wiki Created: 16/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Task Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: documentation, newbie


 Description   

The documentation is nice but something that walks people through the steps would be nicer.






[CLJS-1128] Describe internationalization strategies via Google Closure on the wiki Created: 16/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Task Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: documentation, newbie


 Description   

This can be done via Google Closure defines or via pulling a specific locale. A page should document how this can be done.






[CLJS-1127] validate compiled file written to disk Created: 16/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

If we validate the file written to disk then we can catch common error of running multiple build processes and abort.






[CLJS-1125] Simple corrupted compiled file detection Created: 16/Mar/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

We should include a line at the end of the file that we check for to determine that the file was not corrupted due to either an incomplete write or a clobbered write. It should be be the SHA of the ClojureScript source it was generated from.






[CLJS-1123] this-as unexpectedly binds js/window when used within function with post-condition Created: 15/Mar/15  Updated: 16/Mar/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: J. S. Choi Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Adding a post-condition to any function that uses cljs.core/this-as will unexpectedly cause this-as's "this" symbol to be bound to the root object (e.g., js/window) instead.

(defn f-no-post-condition [argument]
  (this-as this
    (js/console.log argument this)))

(defn f-with-post-condition [argument]
  {:post [true]}
  (this-as this
    (js/console.log argument this)))

(def test-object
  #js {:methodNoPostcondition f-no-post-condition
       :methodWithPostcondition f-with-post-condition})

(f-with-post-condition "A") ; Correctly prints js/window
(.methodNoPostcondition test-object "B") ; Correctly prints test-object
(.methodWithPostcondition test-object "C") ; Incorrectly prints js/window


 Comments   
Comment by David Nolen [ 16/Mar/15 6:17 AM ]

This is almost certainly a different manifestation of CLJS-719.

Comment by Thomas Heller [ 16/Mar/15 6:21 AM ]

Just looked at the generated javascript. As David mentioned the problem is the extra function generated to get the result for the :post condition.

dummy.f_no_post_condition = (function f_no_post_condition(argument){
var this$ = this;
var G__82157 = argument;
var G__82158 = this$;
return console.log(G__82157,G__82158);
});
dummy.f_with_post_condition = (function f_with_post_condition(argument){
var _PERCENT_ = (function (){var this$ = this;
var G__82161 = argument;
var G__82162 = this$;
return console.log(G__82161,G__82162);
})();


return _PERCENT_;
});
dummy.test_object = {"methodWithPostcondition": dummy.f_with_post_condition, "methodNoPostcondition": dummy.f_no_post_condition};
dummy.f_with_post_condition("A");
dummy.test_object.methodNoPostcondition("B");
dummy.test_object.methodWithPostcondition("C");




[CLJS-1109] Record type name and advanced optimization Created: 12/Mar/15  Updated: 12/Mar/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3058
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Daniel Skarda Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

It is not possible to query type name in advanced compilation.
Code below prints correct record name in other compilation modes, but under advanced compilation it prints constructor source code.

(defrecord FooBar [a])

(def fb (FooBar. 1))

(prn (-> fb))
(prn (-> fb type))
(prn (-> fb type pr-str))





[CLJS-1104] Compute SHA for ClojureScript compiled file Created: 10/Mar/15  Updated: 22/Dec/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Needed for shared AOT cache






[CLJS-1091] Compose JavaScript dependency indexes Created: 07/Mar/15  Updated: 29/Apr/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Currently hard coded to Google Closure deps.js and the one produced for a build. Users should be able to supply JS dependency indexes that can get merged in.






[CLJS-1076] :nashorn target Created: 02/Mar/15  Updated: 02/Mar/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None


 Description   

To run well on Nashorn the target should supply CLOSURE_IMPORT_SCRIPT as well as setTimeout or setImmediate for core.async.






[CLJS-1075] Generic inline source map support Created: 02/Mar/15  Updated: 22/Dec/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Currently hard coded to REPLs. Would simplify jsbin and similar integration.






[CLJS-1070] top-level boolean inference does not work Created: 28/Feb/15  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Problem for using boolean Closure defines



 Comments   
Comment by Francis Avila [ 30/Mar/15 12:02 PM ]

I am unsure if this is the same issue, but forms like ^boolean (js/isFinite n) also do not seem to analyze correctly: if, and, and or will still emit a call to truth_.





[CLJS-1067] Shared AOT cache for dependencies in JARs Created: 26/Feb/15  Updated: 27/Feb/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

3rd party library code in JARs shouldn't be recompiled across dev and prod configurations. There should be a shared AOT cache for all builds within a project for all non-project local source.






[CLJS-1059] Simple interface wanted to convert cljs forms to js Created: 22/Feb/15  Updated: 31/Jan/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3211
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Stuart Mitchell Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: analyzer, compiler


 Description   

In our project (a clojurescript debugger) we want to convert cljs forms or a sequence of forms into javascript so that they can be executed in the javascript console.

We would like something similar to closure/compile-form-seq (https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/closure.clj#L308)

However, we need to supply, the namespace requires and locals in an env like this

{:ns {:name "test.core" :requires {(quote gstring) (quote goog.string)}} :locals {}}

This code seems to do what we want.

(defn compile-form-seq
    \"Compile a sequence of forms to a JavaScript source string.\"
    [forms env]
    (env/ensure
    (compiler/with-core-cljs nil
      (fn []
        (with-out-str
            (doseq [form forms]
              (compiler/emit (analyzer/analyze env form))))))))

I am not sure why I need env/ensure.

Would you be able to patch compile-form-seq to provide the needed interface, or suggest what we should be doing.

Thanks
Stu



 Comments   
Comment by Mike Thompson [ 22/Feb/15 10:09 PM ]

Just to be clear:
1. when our debugger is at a breakpoint,
2. the user can type in an expression at the repl
3. in response, our debugger has to compile the user-typed-in expression to javascript (and then execute it, showing a result)
4. taking into account any local bindings. <---- this is the key bit.

To satisfy point 4, our tool extracts all the 'locals' from the current call-frame, and then supplies all these local bindings in env/locals, so the compiler doesn't stick a namespace on the front of them.

For example, if there was a local binding for 'x' in the callstack, and the user's repl-entered-expression involves 'x', then we want the compiler to leave the symbol 'x' alone and to not put some namespace on the front of it. In the final javascript, it must still be 'x', not 'some.namespace.x'

Our method to achieve this is to put 'x' into env/locals when compiling – and it all works. Except, with the recent changes this has become more of a challenge. Hence this ticket asking for a way to pass in env.

Comment by Thomas Heller [ 23/Feb/15 3:19 AM ]

You could wrap the user expression in an fn, that would allow you to skip messing with the locals. The REPL basically does the same trick for *1,*2,...

(fn [x]
  ~user-expression-here)
Comment by David Nolen [ 29/Apr/15 7:21 AM ]

Seems like something useful to add to a cljs.compiler.api namespace.





[CLJS-1048] support function values in static vars compile time metadata Created: 20/Feb/15  Updated: 20/Feb/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Ivan Mikushin Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Function values are currently only supported for :test metadata key as a special case.






[CLJS-1047] externs checking for js/foo Created: 19/Feb/15  Updated: 30/Apr/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3211
Fix Version/s: GSoC

Type: Enhancement Priority: Minor
Reporter: David Nolen Assignee: Maria Geller
Resolution: Unresolved Votes: 1
Labels: None


 Description   

Worth looking into validating `js/foo` forms again the known externs set. Can probably be done by leveraging the Closure JS Parser.



 Comments   
Comment by Michael Griffiths [ 22/Feb/15 12:03 PM ]

Would you consider making the results of parsing available to tooling (e.g. in cljs.env/*compiler*)? I would use this to add support for autocompletion of js/ forms to CIDER.

Comment by David Nolen [ 23/Feb/15 8:15 AM ]

Definitely open to the idea of exposing this information to other tooling when we get there.





[CLJS-1029] Investigate how ns aliasing can be supported in ClojureScript macro files Created: 11/Feb/15  Updated: 11/Feb/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Task Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Currently we just require the file. Perhaps possible to control compilation of the macro file such that ClojureScript ns aliases are established. This may not bear fruit but worth thinking about.



 Comments   
Comment by David Nolen [ 11/Feb/15 4:05 PM ]

Any design ideas along this path needs to keep .cljc files in mind.





[CLJS-994] print a warning when :externs file paths can't be found. Created: 30/Jan/15  Updated: 08/Jun/16

Status: Reopened
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Enhancement Priority: Minor
Reporter: Crispin Wellington Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: cljs, enhancement, errormsgs, patch,
Environment:

Linux 64bit

java version "1.7.0_65"
OpenJDK Runtime Environment (IcedTea 2.5.3) (7u71-2.5.3-0ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)


Attachments: Text File clojurescript-extern-missing-warning.patch    
Patch: Code

 Description   

clojurescript silently ignores missing externs files possibly leading a developer to chase their tail.

Presently it can be very confusing using advanced compilation if you have made a mistake in the path name of one of your :externs files. This patch makes the compiler print a warning on stderr so you can quickly determine the cause of the broken advanced compilation output.

As a side effect, when doing a basic lein-cljsbuild a warning is always printed:

```
WARNING: js resource path closure-js/externs does not exist
```

This is because lein-cljsbuild quietly adds this extra path to your :externs listing without you knowing.



 Comments   
Comment by David Nolen [ 31/Jan/15 1:59 PM ]

You need to bind *out* to *err*, or just print to it directly a la cljs.util/debug-prn.

Comment by Crispin Wellington [ 31/Jan/15 7:30 PM ]

I did bind out to err. Check the patch.

Comment by David Nolen [ 01/Feb/15 12:30 PM ]

Crispin, oops sorry you are correct. Thanks.

Comment by David Nolen [ 13/Mar/15 7:33 AM ]

fixed https://github.com/clojure/clojurescript/commit/5f66a78bf469a9875e51aa39c29d3e66ce890eb4

Comment by David Nolen [ 14/Mar/15 5:55 AM ]

The solution does not work for cljsbuild. It's unclear why there so much machinery in place over the approach taken for deps.clj.

Comment by David Nolen [ 15/Mar/15 10:37 AM ]

Stalled on this cljsbuild issue https://github.com/emezeske/lein-cljsbuild/issues/383

Comment by Crispin Wellington [ 23/Mar/15 2:50 AM ]

This lein-cljsbuild issue is what made me make it just a warning initially, and not a hard error like raising IllegalArgumentException does. Though I agree it should be a hard error. If we start with a warning, it enables the immediate problem for the developer to be resolved, and leaves a wart that the cljs-build project can then see that need fixing on their end. Then when that end is fixed it could be made a hard error. If cljsbuild is fixed fairly soon then all is well, but if it takes a long time, a warning might be a good first step.





[CLJS-981] Better benchmarking infrastructure Created: 17/Jan/15  Updated: 12/Feb/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Task Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: newbie


 Description   

We should use ProcessBuilder to run the various benchmark scripts and control which benchmarks we test and which engines we run. Benchmarks should produce EDN data that can be written to a file, loaded into Incanter, etc.






[CLJS-901] Protocolize compiler access to the file system Created: 03/Dec/14  Updated: 22/Dec/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Task Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None


 Description   

Currently builds are based on files on disk. It is desirable to be able to instead get in memory builds, WebDAV based builds, S3 based builds, etc. Many of these alternative strategies are not in scope for the ClojureScript compiler but this does not mean we should not supply the needed hooks for users to control the behavior.



 Comments   
Comment by Thomas Heller [ 03/Dec/14 9:31 AM ]

This and some other issues opened recently (CLJS-900, CLJS-851, CLJS-899, ...) have some overlap with what I have done in shadow-build [1]. Memory based builds are actually already possible cause it will only touch the disk when asked to, although the API could use some cleanup.

Anyways, might be worthwhile to coordinate these efforts to make CLJS more accessible for everyone.

[1] https://github.com/thheller/shadow-build

Comment by Alan Dipert [ 04/Feb/15 11:36 AM ]

I too think it would be totally awesome to have builds based on sources from disparate places.

One alternative in this spirit I have been thinking about is a "SourceSet" approach. The idea is, instead of teaching CLJS how to consume various place-types directly via protocols, provide an API for building a "SourceSet" value and also a build function that takes the SourceSet as input. I imagine the SourceSet in its simplest form as a map of namespaces to string sources.

With a value to represent sources that is place-agnostic and immutable, 3rd party tools can consume/emit/transform these values before invoking a compile without knowledge or interest in CLJS internals. Conversely CLJS need not be concerned with how SourceSets are constructed.

This whole idea is inspired by boot's FileSets, which work basically the same but can't have the "it fits in memory" assumption.





[CLJS-900] Parameterize caching strategy Created: 03/Dec/14  Updated: 03/Dec/14

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Task Priority: Minor
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Currently the caching strategy is hard coded to a disk based one. It would be desirable in many situations for the caching to be in memory. We should decouple the caching strategy and support disk / memory out of the box.






[CLJS-890] Incorrect behaviour of (str obj) when obj has valueOf method Created: 24/Nov/14  Updated: 25/Apr/15

Status: Reopened
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Nikita Beloglazov Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None

Attachments: Text File cljs-890.patch     File cljs-core-str-perf.diff    

 Description   

Example

(str #js {"toString" (fn [] "hello") "valueOf" (fn [] 42)}) ; => "42"

The problem in the fact that ClojureScript uses concatenation to convert values to strings and that doesn't work well with objects which have valueOf() method overriden.

Example in js:

var obj = {
    toString: function() { return 'hello'; },
    valueOf: function() { return 42; }
};
console.log(String(obj)); => 'hello'
console.log(obj.toString()); => 'hello'
console.log('' + obj); => '42'

Potential solution might be to use String() function. Using toString() won't work as described in this issue: http://dev.clojure.org/jira/browse/CLJS-847



 Comments   
Comment by Kevin Neaton [ 24/Nov/14 10:34 AM ]

Is there a valid use case where toString and valueOf are not in sync? E.g.

(not= (.toString x) (js/String (.valueOf x))

If not, is it "incorrect" for the two methods to be out of sync?

Comment by Nikita Beloglazov [ 24/Nov/14 10:40 AM ]

Here is an example of such use case: https://github.com/processing-js/processing-js/blob/master/src/Objects/Char.js
That's how I found this bug.

Comment by Kevin Neaton [ 24/Nov/14 10:49 AM ]

Thanks for the link. I see what you mean.

Comment by David Nolen [ 02/Dec/14 5:08 AM ]

The problem with going with String appears to be a massive performance hit to printing http://jsperf.com/string-vs-tostring2/6.

Unless a brilliant idea is proposed this seems best solved / worked around in user code.

Comment by Nikita Beloglazov [ 02/Dec/14 6:41 AM ]

Append performs better on strings and numbers, but it performs worse on objects so it is not a clear performance hit. If I heavily work with objects and use (str) to convert them into strings then I actually lose on performance with current implementation.
Anyway current implementation of str is incorrect as it doesn't honor toString method. And this is what str function supposed to do. I believe a compiler should be correct first and then worry about performance.

Comment by David Nolen [ 02/Dec/14 7:38 AM ]

Sorry going back over this I believe the issue is really that we simply need to backout CLJS-801.

Comment by David Nolen [ 02/Dec/14 7:41 AM ]

reverted CLJS-801 in master

Comment by Francis Avila [ 02/Dec/14 10:32 AM ]

CLJS-801 only deals with the str macro. Aren't we still going to have str function problem because of CLJS-847? https://github.com/clojure/clojurescript/commit/08b4b1585cf0ef739e903985ee4c6b7fc6c47642 (also Yet if we use toString there, Safari 6.0.5 blows up. Maybe we need {{[o].join('')}}? Depending on where the bug is this may be wrong in Safari 6.0.5 too.

What we need to do very specifically is somehow get the return value of the (in ECMASCRIPT-ese) ToString abstract operation on the object (or the underlying ToPrimitive abstract operation with the String hint). String concat with the add operator

Options as I see it are:

  • x.toString() : Bad because of CLJS-847
  • {{[x].join('')}} : Should work (and does right thing for null/undefined), but I think we should test in Safari 6.0.5. Also very slow.
  • String
  • String.prototype.concat
  • String.prototype.slice(x,0) String.prototype.substring(x,0) String.prototype.substr(x, 0)
  • x.toString() normally, but String if we detect that we'll trigger CLJS-847. (Can specialize on startup.)
Comment by David Nolen [ 02/Dec/14 10:35 AM ]

Is there any evidence that higher usage of str is actually problematic?

Comment by Francis Avila [ 02/Dec/14 10:44 AM ]

String concat using the addition operator uses an un-hinted ToPrimitive abstract call (which will try x.valueOf() first then x.toString(), usually) and then {{ToString}}s the result of that, so it's not an option unless we are concating primitive values.

Details:

Comment by David Nolen [ 02/Dec/14 10:50 AM ]

I'm not really all that concerned about the specification, only if it matters in the wild. If this doesn't affect Safari 6.05 we don't care.

Comment by Francis Avila [ 02/Dec/14 11:01 AM ]

Is there any evidence that higher usage of str is actually problematic?

Kevin Neaton, who opened CLJS-847, was using a patch in production which only addressed the higher order case and he said the patch fixed the issue for them. He was unaffected by the str macro case because it either used ''+x already (with CLJS-801 applied) or it used {{[x].join('')}} (which hasn't been tested with Safari 6.0.5 yet, but probably works).

So if we had a problem using ''+x with the str macro, we will certainly have a problem with ''+x with a string function as long as CLJS-847 is applied.

I haven't pulled down master yet, but here is a test case which I bet will fail with the CLJS-847 patch:

(def tricky-obj #js {"toString" (fn [] "hello") "valueOf" (fn [] 42)})
(assert (= (apply str tricky-obj) "hello")) ;; will get "42"
Comment by Francis Avila [ 02/Dec/14 11:09 AM ]

I'm not really all that concerned about the specification, only if it matters in the wild. If this doesn't affect Safari 6.05 we don't care.

To be clear, there are two issues here:

CLJS-847: x.toString() fails on Safari 6.0.5. Workaround is ''+x (only done in str macro case).
CLJS-890: ''+x doesn't give expected results for objects which define valueOf. Expectation is that x.toString() is called, instead x.valueOf().toString(). Fix is to use array join instead of string concat in str macro, but it doesn't address the ''+x workaround from CLJS-847.

To make matters worse, it looks like the toString() error on Safari may only be triggered at certain JIT levels!

Comment by Francis Avila [ 02/Dec/14 11:10 AM ]

Workaround is ''+x (only done in str macro case).

I mean "Workaround is ''+x (only done in str function case)."

Comment by Nikita Beloglazov [ 08/Dec/14 6:14 PM ]

Can this bug be reopened meanwhile? If I understand correctly the fix should affect https://github.com/clojure/clojurescript/commit/08b4b1585cf0ef739e903985ee4c6b7fc6c47642 but this code still present in HEAD.

Comment by David Nolen [ 08/Dec/14 6:37 PM ]

We've switched to goog.string.buildString in master https://github.com/clojure/clojurescript/commit/94eb8a960fef6aaca4ba44b251cefbfa04d0f6ac

Comment by Nikita Beloglazov [ 08/Dec/14 8:32 PM ]

Yes, that works. Cool, thanks!

Comment by Thomas Heller [ 01/Jan/15 7:12 AM ]

Sorry for re-opening.

I was doing some profiling of my code and noticed a warning in the profiling output about cljs.core/str.

Chromes complains with: "Not optimized. Bad value context for arguments value", looking further at the implementation of goog.string.buildString

goog.string.buildString = function(var_args) {
  return Array.prototype.join.call(arguments, '');
};

Given that we don't ever call it with more than one argument it is probably not best implementation choice.

Maybe skip the call and inline it ala

(defn str
  "With no args, returns the empty string. With one arg x, returns
  x.toString().  (str nil) returns the empty string. With more than
  one arg, returns the concatenation of the str values of the args."
  ([] "")
  ([x] (if (nil? x)
         ""
         (.join #js [x] "")))
  ([x & ys]
    (loop [sb (StringBuffer. (str x)) more ys]
      (if more
        (recur (. sb  (append (str (first more)))) (next more))
        (.toString sb)))))

I didn't follow this issue but why are we not using .toString? The buildString/array approach seems kind of hackish?

I'm not too sure about the overall impact but since cljs.core/str showed up pretty high in my profile I think this should be investigated further.

Comment by Thomas Heller [ 01/Jan/15 7:28 AM ]

Before:

;;; str
[], (str "1"), 1000000 runs, 254 msecs
[], (str 1), 1000000 runs, 266 msecs
[], (str nil), 1000000 runs, 80 msecs
[], (str "1" "2" "3"), 1000000 runs, 753 msecs

After:

;;; str
[], (str "1"), 1000000 runs, 82 msecs
[], (str 1), 1000000 runs, 86 msecs
[], (str nil), 1000000 runs, 79 msecs
[], (str "1" "2" "3"), 1000000 runs, 242 msecs

But I only tested V8, probably needs some verification.

Comment by Thomas Heller [ 01/Jan/15 7:39 AM ]
(defn str
  "With no args, returns the empty string. With one arg x, returns
  x.toString().  (str nil) returns the empty string. With more than
  one arg, returns the concatenation of the str values of the args."
  ([] "")
  ([x1]
     (.join #js [x1] ""))
  ([x1 x2]
     (.join #js [x1 x2] ""))
  ([x1 x2 x3]
     (.join #js [x1 x2 x3] ""))
  ([x1 x2 x3 x4]
     (.join #js [x1 x2 x3 x4] ""))
  ...)

Does perform even better.

;;; str
[], (str "1"), 1000000 runs, 40 msecs
[], (str 1), 1000000 runs, 43 msecs
[], (str nil), 1000000 runs, 96 msecs
[], (str "1" "2" "3"), 1000000 runs, 117 msecs

How many args should it inline?

Comment by David Nolen [ 01/Jan/15 12:43 PM ]

I'd be OK with up to 4 then variadic.

Comment by Thomas Heller [ 01/Jan/15 5:05 PM ]

There is some weird interaction between the code generated by the cljs.core/str macro and function.

The macro generates

(str "hello" 1 "world" :yo nil)

yields

[cljs.core.str("hello"),cljs.core.str((1)),cljs.core.str("world"),cljs.core.str(new cljs.core.Keyword(null,"yo","yo",1207083126)),cljs.core.str(null)].join('');

Given that str with 1 arg will basically unroll to

[["hello"].join(""), ...]

I think it might be safe to completely remove the macro since cljs.core/str would then do the same and the JIT is probably smart enough to figure this out (or even Closure when compiling).

Comment by Francis Avila [ 02/Jan/15 11:14 AM ]

Chromes complains with: "Not optimized. Bad value context for arguments value", looking further at the implementation of goog.string.buildString

Chrome complains about the variadic function dispatch code in the same way, see CLJS-916 plus patch.

I think it might be safe to completely remove the macro since cljs.core/str would then do the same and the JIT is probably smart enough to figure this out (or even Closure when compiling).

The Closure compiler is not smart enough to remove the intermediate array, which is why I filed CLJS-801 (which this ticket rolled back). I don't think JITs can do it either.

I am beginning to wonder if we should ignore the Safari 6.0.5 problem in CLJS-847 that started all this string mess. To recap:

  1. CLJS-801 is accepted, which removes [123, x].join('') in the str macro case in favor of ''+123+(cljs.core/str$arity$1 x) style code, which the closure compiler can precompute. At this time, the one-arg cljs.core/str function (not macro) calls toString on its argument.
  2. CLJS-847 is filed. On Safari 6.0.5 at higher JIT levels, calling toString on some things (possibly only unboxed numbers? definitely not strings) throws a TypeError. This is unquestionably a bug in Safari. David fixes by making one-arg cljs.core/str function call js-str instead of toString. js-str uses string-concat ''+x.
  3. However, this breaks for objects that define valueOf (issue in current ticket), because in js ''+x is the same as ''+x.valueOf().toString() not ''+x.toString().
  4. David considers using String() and variations but rejects because of performance hit.
  5. David rolls back CLJS-801 from the string-concat back to the array-join style to fix.
  6. Nikita and I point out that rolling back CLJS-801 only fixes the str macro, not the string function, which still uses js-str and hence string-concat.
  7. David fixes the str function to use goog.string.buildString, which has the behavior of array.join. Behavior is now correct even on Safari 6.0.5.
  8. Thomas points out that buildString uses arguments in a way unoptimizable by v8, and now the str function (not macro) has a performance regression. He suggests using [].join() directly.

So, there's a lot of back and forth on this issue, and it's all because of a bug in Safari 6.0.5 which no one has been able to reproduce first-hand because Safari 6.0.5 is old and rare. For some perspective, Safari 6.0.x was only available on Lion and Mountain Lion between July 25,2012 and June 11,2013. Before July 25,2012 Lion used Safari 5.1.x and there was no Mountain Lion. On June 11, 2013, both Lion and Mountain Lion switched to Safari 6.1.x which does not suffer from the toString TypeError bug (I checked--I have an iMac with Lion on it). The only machines on Safari 6.0.5 are (Mountain) Lion machines which used software updates until late 2012-early 2013 and then stopped. I can't imagine this is a large number of people.

It is theoretically possible for me to run Safari 6.0.x on my Lion machine to actually test this, but I can't find a way to downgrade from 6.1.x.

I think the options are:

  1. Use array.join() for all stringification and take the performance hit (which we should quantify). Include a comment that this is only for Safari 6.0.x (only confirmed second-hand on 6.0.4 and 6.0.5) for future generations, who are going to think this is weird.
  2. Use CLJS-801 and toString (status quo before CLJS-847), and ignore this problem for Safari 6.0.x.
  3. Use CLJS-801, but add a number? check (with comment) to cljs.core/str$arity$1 for Safari 6.0.5. The number case should use js-str, and the rest toString. I think this will work, but again we have no way to test--we really need to get our hands on a Safari 6.0.x browser.

Of course we should benchmark these approaches but my hunch is that 2 is faster than 3 is faster than 1.

Comment by David Nolen [ 02/Jan/15 11:16 AM ]

We are not going to ignore Safari 6.0.X. Any decisions made about this ticket will include supporting it.

Comment by Francis Avila [ 10/Jan/15 4:12 AM ]

Update on some research I am doing into this.

I created a jsperf of alternative str implementations that I am trying out. Right now I've only looked at one-arg str. I discovered a few things:

  • {{''+[x]}} is a faster alternative to [x].join('').
  • Advanced compilation can compute {{''+[x]}} at compile time if x is a bool, str, undefined, null, or number, even through function calls! I.e. str_test.str_arr(123) compiles to "123" without macro magic.
  • However, using an intermediate array (even if a preallocated singleton) is still slower than the old (if (nil? x) "" (.toString x))
  • Using a switch statement is as least as fast as the str-tostr baseline, usually faster.
  • I am 99% sure all these implementations (except str-tostr, the baseline, which definitely fails) work on the dreaded Safari 6.0.x. If anyone has this version, point it at the jsperf link above and run the tests. I think Browserstack has this version of Safari.

I'm still investigating the variadic case (str x y z a b c). It might be better to use reduce instead of Stringbuffer+seq. (Stringbuffer just does ''+x now instead of an array-join.)

Comment by Thomas Heller [ 10/Jan/15 6:37 AM ]

Sorry, got side-tracked for a bit.

@Francis: Thanks for the recap.

Don't have Safari 6 available either, but seems wrong that we all have to suffer because is minor percentage still has this (667 users of 190k+ on my site). Don't have a solution cause I can't test whether it works, we might try String.concat.

"".concat(obj); // "42"
"".concat(obj, ""); // "hello"
String.prototype.concat(obj, "") // "hello"
String.prototype.concat("", obj) // "hello"

But no idea if String.concat works, also it behaves odd with regards to valueOf.

http://jsperf.com/js-string-concat-variants

Perf is also inconclusive since Firefox appears to be cheating.

Comment by Francis Avila [ 10/Jan/15 2:04 PM ]

Tested that jsperf with Safari 6.0.5 using Browserstack, results are there.

Note I could not reproduce CLJS-847 because str-tostr does not fail as expected. I will try harder now that I have a browser to test.

Comment by Francis Avila [ 10/Jan/15 6:55 PM ]

Still cannot reproduce CLJS-847.

This script includes my attempt at a minimum reproducible case. My theory was that certain types at higher jit levels would fail. I could not get any case to fail. I also tried flapping back and forth between types and using one type at a time, but still no failures.

In this thread I found this "minimal" script which the OP said he could get to fail reliably. I could not get it to fail. However the original post was from feb 15, 2013, which means the Safari he was using would have to be 6.0.2 or lower.

Hypotheses:

  1. This error does not affect 6.0.5 but maybe 6.0.4 or lower.
  2. BrowserStack's system somehow mitigates the bug, meaning we need a "real" Lion Safari 6.0.x to test.
  3. These tests only fail under the correct phase of the moon.

So I can code up a patch for str using the str-switch implementation (which is at least a bit faster on some browsers), but I have no idea if it may fail on Safari 6.0.5. I only know that it works so far. CLJS-801 should also be safe to reapply because the root cause of all issues is the implementation 1-arity of the cljs.core/str function.

I have also asked for Kevin's help back in CLJS-847. (Kevin was the original reporter of the Safari 6.0.x issue.)

Comment by Francis Avila [ 19/Jan/15 12:51 AM ]

Made a jsperf of variadic cases. Chrome seems to really prefer IReduce to seq+stringbuilder for vectors (other collections not tested), but there is no difference or a small slowdown on other browsers. Not sure if it's worth it.

Also updated arity-one cases with a str using switch and never using toString. Nearly 50% slower than using switch or toString on Chrome, smaller on Safari.

In terms of safety str-switch-notostr does not use toString at all so is probably safer. I think str-switch will likely work too, though, and is significantly faster. However I haven't been able to get any TypeErrors in Safari 6.0.5 so it's anyone's guess.

I suggest something like this as a new str (which doesn't use reduce, but could):

(defn str
 ([x]
  (case (js* "typeof ~{}" x)
   "string" x
   "object" (if (identical? nil x) "" (.toString x))
   ("boolean" "number") (js-str x)
   "undefined" ""
   (js-str #js [x])))                                       ;; insurance against Safari 6.0.x TypeError bug.
 ([a b] (js* "~{}+~{}" (str a) (str b)))
 ([a b c] (js* "~{}+~{}+~{}" (str a) (str b) (str c)))
 ([a b c d] (js* "~{}+~{}+~{}+~{}" (str a) (str b) (str c) (str d)))
 ([a b c d & more]
  (loop [s (str a b c d) [e f g h & r] more]
   (let [s' (js* "~{}+~{}+~{}+~{}+~{}" s e f g h)]
    (if (nil? r)
     s'
     (recur s' r))))))
Comment by Francis Avila [ 19/Jan/15 11:24 PM ]

First cut of a possible patch that resolves this while not breaking CLJS-847. Should wait for confirmation that this does not break Safari 6.0.x.

Comment by Francis Avila [ 19/Jan/15 11:34 PM ]

Oops forgot tests.

Comment by Francis Avila [ 03/Feb/15 10:24 AM ]

Update in CLJS-847: original reporter was not able to reproduce his original bug report in Safari 6.0.x running in BrowserStack. This may be because of BrowserStack, but it's the best we have.

Given how hard this bug is to reproduce, how few people it affects, and how significant the performance regression is, I still think we should go back to the simple (if (nil? x) "" (.toString x)) implementation. However, you could also try the patch on this ticket (using a typeof switch), which at least (handwaving) might fix this bug in Safari 6.0.x and is a little faster than a simple .toString in Chrome and not much slower elsewhere. (The reason I think it might avoid this bug in Safari is that it avoids calling .toString on non-Objects.)

Comment by Antonin Hildebrand [ 25/Apr/15 11:08 PM ]

I wonder if you considered swapping str function at runtime during CLJS init phase.

Implement str function using plain .toString() call (original solution). And at startup check for Safari 6.0.x presence and optionally swap str for implementation wrapping .toString() call in a try-catch block silencing TypeError exceptions by falling back to Safari 6.0.x friendly .toString() alternative.

We would get correct semantics in all cases. And price would be just slower printing execution on Safari 6.0.x not on all systems.





[CLJS-871] .-default property access returns nil Created: 11/Oct/14  Updated: 08/Jun/16

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.145
Fix Version/s: Next

Type: Defect Priority: Minor
Reporter: Joel Holdbrooks Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None

Attachments: Text File 871.patch     Text File