<< Back to previous view

[CLJS-1438] If .startsWith (or any other method) is used on a clojurescript string object when using :optimizarions :advanced code does not work Created: 31/Aug/15  Updated: 31/Aug/15  Resolved: 31/Aug/15

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

Type: Defect Priority: Major
Reporter: Felipe Gerard Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: compiler, interop
Environment:

MAC OS


Attachments: File minimal.tgz    
Waiting On: Alex Miller

 Description   

This is a minimal project HelloWorld (I am attaching a full project "minimal.tgz"):
(ns minimal.core)

(let [elem (.getElementById js/document "app")]
(set! (.-innerHTML elem) (str "Hello World " (.. "Hello" (startsWith "H")))))

Will run fine with no optimizarions but when using :advanced I get: Uncaught TypeError: "Hello".Lb is not a function



 Comments   
Comment by David Nolen [ 31/Aug/15 3:31 PM ]

This is not a bug. That's how Closure advanced optimization works. Use goog.string/startsWith instead.





[CLJS-1432] 1.7.48 assigns same value for '$ and '. symbols under advanced Created: 24/Aug/15  Updated: 31/Aug/15  Resolved: 27/Aug/15

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

Type: Defect Priority: Minor
Reporter: Nikita Prokopov Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File cljs-1432-symbols-collision-2.patch     Text File cljs-1432-symbols-collision.patch    

 Description   
(println :+ :a :$ :. :a$b :a.b :z)
      => :+ :a :. :. :a$b :a$b :z

(println '+ 'a '$ '. 'a$b 'a.b 'z)
      =>  +  a  .  .  a$b  a$b  z

expected:
(not= :$ :.)
(not= '$ '.)

Related:
CLJS-1105
https://github.com/clojure/clojurescript/commit/d92b3004046d1f83ab76a2b94f4129cb16e47848
https://github.com/tonsky/datascript/issues/108



 Comments   
Comment by Nikita Prokopov [ 26/Aug/15 12:09 AM ]

Just wanted to add that this issue breaks DataScript to the point it’s impossible to use (because it relies on both '$ and '.). DataScript cannot be used at all with 1.7.48, 1.7.58, 1.7.107

Comment by David Nolen [ 26/Aug/15 10:38 PM ]

Why not just special case '. here?

Comment by Nikita Prokopov [ 27/Aug/15 3:34 AM ]

David, I’m not sure what is the purpose of this code, e.g. why it transforms symbol/keyword name at all. If this is going to constants table as a key, it can be any string, right?

My solution just eliminates other possible collisions (e.g. :a$b vs :a.b) because I think it is not fun to encounter stuff like this. It takes really huge effort to debug, because it comes from the most unexpected place: on the one hand, language guarantees keywords are only equal to themselves, on the other, it silently treats different keywords/symbols as the same.

If you think my solution is an overkill, or if there are technical reasons it can’t be applied, as I’m not aware of all the nuances, I leave this decision to you.

Other possible solution might be, if encountered kw/sym that might conflict, do not apply this optimization to them at all. We would probably lose some performance, but keep the guarantees.

Comment by David Nolen [ 27/Aug/15 6:22 AM ]

Emitting strings for the constants table defeats minification and dead code elimination.

The bug is a simple source code generation symbol clash that can be avoided with special casing '.. If you can come up with other problematic cases then I'll consider an alternative. Otherwise this is the only patch we will take.

Comment by Nikita Prokopov [ 27/Aug/15 1:10 PM ]

Attached second patch, as you suggested

Comment by David Nolen [ 27/Aug/15 3:31 PM ]

fixed https://github.com/clojure/clojurescript/commit/c5e088786acbdfa5074199f1c5baa9fa7c30f5a6

Comment by Nikita Prokopov [ 27/Aug/15 3:40 PM ]

Thanks David!

Comment by David Nolen [ 29/Aug/15 7:32 AM ]

I just cut a pre-release 1.7.122 with this fix.

Comment by Nikita Prokopov [ 31/Aug/15 12:14 AM ]

Confirming, works like a charm for me





[CLJS-1436] self-host: Doesn't load dep ns when loading own cache Created: 29/Aug/15  Updated: 30/Aug/15

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

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


 Description   

Background: Both *load-fn* and *eval-fn* support the concept of passing JavaScript and analysis cache info. Such data passed to *eval-fn* should be able to be stored and subsequently passed to *load-fn* upon subsequent runs in order to effect analysis/compilation caching.

If one namespace depends on another then transitive loading occurs if no caching is involved. But if cached information it returned to *load-fn* when the top-level namespace is loaded, then transitive loading does not occur.

My speculation is that this is the case because, while the analysis cache is incorporated here

https://github.com/clojure/clojurescript/blob/r1.7.122/src/main/cljs/cljs/js.cljs#L206-L208

nothing further is done to load dependent namespaces as is done when ClojureScript source is passed in the callback.

Here is a concrete example. Let foo.core depend on bar.core:

(ns foo.core
  (:require bar.core))

(prn bar.core/x)
(ns bar.core)

(def x 2)

If you require foo.core (via an ns form passed to eval-str), then *load-fn* is called with

{:name foo.core, :macros nil, :path "foo/core"}

and it is called back upon with the loaded ClojureScript source:

{:lang :clj, :source "(ns foo.core\n  (:require bar.core))\n\n(prn bar.core/x)\n"}

and the embedded :require then causes *load-fn* to be invoked for bar.core:

{:name bar.core, :macros nil, :path "bar/core"}

with its source being passed to the callback:

{:lang :clj, :source "(ns bar.core)\n\n(def x 2)\n"}

Then the evaluation phase begins with two evaluations (done properly in reverse dependency order):

{:lang :clj, :name bar.core, :path "bar/core", :source "goog.provide(\"bar.core\");\nbar.core.x = 2;\n", :cache {:use-macros nil, :excludes #{}, :name bar.core, :imports nil, :requires nil, :uses nil, :defs {x {:name bar.core/x, :file nil, :line 3, :column 1, :end-line 3, :end-column 7, :meta {:file bar.core, :line 3, :column 6, :end-line 3, :end-column 7}}}, :require-macros nil, :doc nil}}

and

{:lang :clj, :name foo.core, :path "foo/core", :source "goog.provide(\"foo.core\");\ncljs.core.prn.call(null,bar.core.x);\n", :cache {:name foo.core, :doc nil, :excludes #{}, :use-macros nil, :require-macros nil, :uses nil, :requires {bar.core bar.core}, :imports nil}}

which causes 2 to be printed.

Then if you start fresh, but go through the same sequence, consuming cached information, you start off the same with *load-fn* being called the same way:

{:name foo.core, :macros nil, :path "foo/core"}

and with the callback now being passed cached information back in return:

{:lang :js, :source "goog.provide(\"foo.core\");\ncljs.core.prn.call(null,bar.core.x);\n", :cache {:name foo.core, :doc nil, :excludes #{}, :use-macros nil, :require-macros nil, :uses nil, :requires {bar.core bar.core}, :imports nil}}

This JavaScript is evaluated by cljs.js here

https://github.com/clojure/clojurescript/blob/r1.7.122/src/main/cljs/cljs/js.cljs#L205

and this causes this to be printed

Can't find variable: bar

Perhaps this isn't a defect if instead it is expected that clients of cljs.js handle the loading of dependent namespaces when caching is involved.



 Comments   
Comment by David Nolen [ 30/Aug/15 10:34 AM ]

A patch for this is welcome. Analysis caches for a ns include all the information needed to load lib and macro dependencies.





[CLJS-1437] Recompile dependents broken when files are not inside one directory Created: 30/Aug/15  Updated: 30/Aug/15

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

Type: Defect Priority: Major
Reporter: Juho Teperi Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: build


 Description   

When a file is changed files dependent on that file are marked for recompile. This doesn't seem to work when the changed file is not in the directory which is passed to build and the directory where dependent namespace is.

Looks like this caused by how build and compile-file work:

  • Build calls -compile -> compile-dir -> compile-root which sorts the files in dependency order and calls -compile for the files in dependency order
    • core ns is compiled
  • Build calls add-depedencies -> add-dependencies -> get-compiled-cljs -> -compile -> compile-file
    • test ns is compiled, core ns is marked as dependent on test ns but as core ns is already compiled this doesn't matter

I believe this effectively breaks :recompile-dependents in cases all cases but one where all the sources are inside one directory. In multiple directory case order of directories passed to cljs.build.api/inputs affects the order on which compile is called so it's possible that some dependent namespaces are compiled before namespaces they depend on are compiled.

Original problem was that added tests were not picked up by run-tests but I have since reproduced this without cljs.test.

The minimal way to reproduce this needs two namespaces, where one depends on another. To display that the dependent namespace is not recompiled, it will print some compile time metadata from the another namespace.

src/hello/core.cljs

(ns hello.core
  (:require hello-world.test))

(enable-console-print!)

(println (:foo (meta #'hello.test/a)))

src2/hello/test.cljs

(ns hello.test)

(def ^{:foo :bar} a "foobar")

To build:

(require '[cljs.build.api :refer [build]])

(def opts {:main "hello.core"
           :output-to "out/main.js"
           :output-dir "out"
           :optimizations :none
           :verbose true})

(defn broken []
  (build "src" opts))

(defn working []
  " Note: ordering of the dirs matters
  (build (inputs "src2" "src" opts)))

To trigger the problem, the metadata needs to be changed after running the build once. Changing test ns doesn't cause core ns to recompiled and changing the metadata doesn't have any effect to println on core ns.






[CLJS-1428] Add a cljs.core/*command-line-args* var Created: 16/Aug/15  Updated: 30/Aug/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 [ 30/Aug/15 10:39 AM ]

I wouldn't mind trying to implement this. I think I need some help.





[CLJS-1400] self-host: doseq does not behave as expected Created: 07/Aug/15  Updated: 29/Aug/15  Resolved: 29/Aug/15

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

Type: Defect Priority: Major
Reporter: Chris Truter Assignee: Chris Truter
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File 1400.patch     Text File infinite_loop2.patch     Text File infinite_loop.patch    
Approval: Vetted

 Description   

When a doseq appears in non-terminal position inside a do block, it "iterates" repeatedly over the first element only.

This only affects bootstrapped clojurescript, but can be supported with a vanilla cljs.js setup and the last example below.

Correctly prints 1 and then 2:

(do :before (doseq [x [1 2]] (println x)))

Correctly does not iterate, and returns :after

(do (doseq [x []] (println x)) :after)

Incorrectly prints 1 ad infinitum

(do (doseq [x [1 2]] (println x)) :after)

Example code including bootstrapping:

(def source "(do (doseq [e [1 2]] (println e)) 3)")

(cljs/eval-str
  (cljs/empty-state)
  source
  source
  {:eval       cljs/js-eval
   :source-map false
   :verbose    true}
  prn)

For convenience a repo to run this example is here:

https://github.com/crisptrutski/do-doseq-bootstrapped



 Comments   
Comment by David Nolen [ 07/Aug/15 6:38 AM ]

We do not take issues that involve 3rd party projects. This ticket needs to reproduced with ClojureScript only before proceeding any further.

Comment by David Nolen [ 07/Aug/15 7:14 AM ]

Reproduced. The patch should include a test case addition to src/test/self/self_host/test.cljs.

Comment by Joel Martin [ 07/Aug/15 9:55 AM ]

Here is a patch that adds a test which reproduces the problem.

Comment by Joel Martin [ 07/Aug/15 10:00 AM ]

Update patch with corrected success condition.

Comment by Chris Truter [ 12/Aug/15 8:18 AM ]

Added slightly more minimal test, that excludes do (still not a great test), and solve the issue by partially reverting this optimization

Perhaps

(-hash (select-keys binding [:name :shadow]))
is sufficient and more performant?

Comment by David Nolen [ 12/Aug/15 8:46 AM ]

If you're are going to mention commits, please link to them thanks.

Comment by David Nolen [ 29/Aug/15 10:00 AM ]

See also CLJS-1435

Comment by Chris Truter [ 29/Aug/15 10:33 AM ]

Mind if I close this as superceded by http://dev.clojure.org/jira/browse/CLJS-1435?

The latter makes a better test case (failure terminates...)

Comment by David Nolen [ 29/Aug/15 11:40 AM ]

fixed https://github.com/clojure/clojurescript/commit/3636a80c9b683cdc1190bd60910bf805e6ab22c0

Comment by Mike Fikes [ 29/Aug/15 2:54 PM ]

Confirmed that fix works downstream in Planck.





[CLJS-1435] self-host: lexical scope is broken Created: 29/Aug/15  Updated: 29/Aug/15  Resolved: 29/Aug/15

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

Type: Defect Priority: Major
Reporter: Chris Truter Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: bootstrap

Attachments: Text File 1435.patch    
Patch: Code and Test

 Description   

This has the cause as http://dev.clojure.org/jira/browse/CLJS-1400, but demonstrates a more provocative compilation error.

Sample code using cljs.js to demonstrate the failure:

(cljs/eval
  (cljs/empty-state)
  '(let [x 1]
     (let [x (inc x)]
       (prn x))
     (prn x))
  {:eval (juxt (comp prn :source) cljs/js-eval)}
  (fn [& _]))

Which displays both the code (line breaks unescaped here for convenience) and resulting error:

"var x_22 = 1;
 var x_23__$1 = (x_23 + 1);
 cljs.core.prn.call(null,x_23__$1);
 cljs.core.prn.call(null,x_22);"
VM1654:2 Uncaught ReferenceError: x_23 is not defined

This issue is more noticeable than in the case encountered via "doseq", as the naming error occurs in a read position rather than write, so we get an error instead of unexpected behaviour.

The patch attached fixies this and 1400



 Comments   
Comment by Chris Truter [ 29/Aug/15 10:32 AM ]

Attached patch with a (hopefully) faster alternate

Comment by David Nolen [ 29/Aug/15 11:00 AM ]

fixed https://github.com/clojure/clojurescript/commit/ed01ddf9a87eb67aa50feacd72e8aa66083cdbfc





[CLJS-1434] clojure.walk no longer preserves meta on vectors Created: 29/Aug/15  Updated: 29/Aug/15  Resolved: 29/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Description   
(require '[clojure.walk :as walk])
(-> (walk/prewalk 
      identity [1 (with-meta [1 2] {:foo 1})])
  (nth 1)
  meta) ;; nil instead of {:foo 1}


 Comments   
Comment by David Nolen [ 29/Aug/15 7:15 AM ]

The problem is that there is an IMapEntry case which will capture vectors and not preserve their meta. But the IMapEntry case is redundant anyway as as map entries are just vectors and these will be handled by the coll? branch of the switch.

Comment by David Nolen [ 29/Aug/15 7:18 AM ]

fixed https://github.com/clojure/clojurescript/commit/1e626ddeeebb748701e113e60032f244209de589





[CLJS-1407] Exposing output file dependency graph in API Created: 09/Aug/15  Updated: 28/Aug/15

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

Type: Enhancement Priority: Major
Reporter: Juho Teperi Assignee: Juho Teperi
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.





[CLJS-1304] Behavior of clojure.string/replace differs from Clojure Created: 09/Jun/15  Updated: 27/Aug/15  Resolved: 27/Aug/15

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

Type: Defect Priority: Major
Reporter: Luke VanderHart Assignee: Andrew Rosa
Resolution: Completed Votes: 0
Labels: None

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

 Description   

When using clojure.string/replace with a function as the replacement value, and using a regular expression with capturing groups, the behavior differs between Clojure and ClojureScript. In Clojure, the replacement fn is passed a tuple of the match and capturing group. In ClojureScript, it is only passed the match value.

In Clojure:

=> (println (str/replace "foobar" #"f(o)o" #(do (println %) "X")))
[foo o]
"Xbar"

In ClojureScript:

=> (println (str/replace "foobar" #"f(o)o" #(do (println %) "X")))
foo
Xbar


 Comments   
Comment by Daniel Woelfel [ 11/Jun/15 3:31 AM ]

If you're looking for a workaround, you can still get the match result from the 2nd arg:

=> (println (clojure.string/replace "foobar" #"f(o)o" (fn [& args] (do (println args) "X"))))
(foo o 0 foobar)
Xbar
Comment by Andrew Rosa [ 13/Jun/15 12:40 PM ]

Implementation and tests for the requested behaviour. Despite of the `vec` not being strictly necessary, I stick with it so the behaviour will be identical on both implementations.

Comment by David Nolen [ 13/Jun/15 5:07 PM ]

Is there any reason here to not just copy the Clojure implementation?

Comment by Andrew Rosa [ 13/Jun/15 5:25 PM ]

Hi David,

Clojure's impl was the first place that I look for, but this feature in special is written against the underlaying Java libraries. You can check it here.

Studying the Clojure implementation I found that there are some of re-* family of functions missing on ClojureScript side, and unfortunately they are heavily inspired on Java's Regex structure (return matchers, find and extract groups). MAYBE we can something like them to ClojureScript, but I don't know the real value there. Anyway, I think that port them or not is subject for another ticket - if you want that I investigate more, I could create it.

Comment by Francis Avila [ 13/Jun/15 5:47 PM ]

I made a proposal some time ago to bring clj and cljs re handling closer together: CLJS-776

Comment by David Nolen [ 13/Jun/15 8:51 PM ]

Andrew, was just looking for rationale. What you've done looks OK to me. People should try the patch and give feedback. Thanks!

Comment by Andrew Rosa [ 13/Jun/15 10:41 PM ]

Hi David, I understand and appreciate your concerns I just want to make clear what I've search and done, sorry if I sound rude - maybe I stepped on my language limitations.

Francis, really this re-* thing is tricky to deal with. I will take a closer look onto your ticket and see if I can add any idea.

Thanks everyone!

Comment by David Nolen [ 27/Aug/15 5:54 AM ]

This patch needs to be rebased to master.

Comment by Andrew Rosa [ 27/Aug/15 9:50 AM ]

Rebased patch against master.

Comment by David Nolen [ 27/Aug/15 11:14 AM ]

fixed https://github.com/clojure/clojurescript/commit/3e146905999f9b9ffb249e139e266f845c4af34c





[CLJS-1430] self-host: .toString is emitted as .toString$ (munged) Created: 18/Aug/15  Updated: 27/Aug/15  Resolved: 27/Aug/15

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

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: bootstrap

Attachments: Text File CLJS-1430-2.patch     Text File CLJS-1430-3.patch     Text File CLJS-1430-4.patch     Text File CLJS-1430.patch    
Patch: Code and Test

 Description   

If you evaluate a form like (.toString "a") in bootstrapped mode, the emitted JavaScript will end up with a dollar-sign: "a".toString$().

This can be reproduced via a compile-str unit test involving the form.



 Comments   
Comment by Mike Fikes [ 18/Aug/15 11:29 PM ]

This is because, even though the compiler indicates that there are no reserved words (the empty set for second argument here):

https://github.com/clojure/clojurescript/blob/v1.7/src/main/clojure/cljs/compiler.cljc#L1006

cljs.core/munge is called here

https://github.com/clojure/clojurescript/blob/v1.7/src/main/clojure/cljs/compiler.cljc#L108

to imitate Clojure's munge. But cljs.core/munge introduces the dollar-sign for reserved JavaScript keywords:

https://github.com/clojure/clojurescript/blob/v1.7/src/main/cljs/cljs/core.cljs#L9898

The direct ClojureScript imitation of Clojure's munge is munge-str here

https://github.com/clojure/clojurescript/blob/v1.7/src/main/cljs/cljs/core.cljs#L9882

Calling that (private) method instead fixes the problem.

Comment by Mike Fikes [ 18/Aug/15 11:37 PM ]

The attached file fixes the issue.

On problem with it, though it it has the compiler calling a private function in cljs.core namespace.

Comment by Mike Fikes [ 18/Aug/15 11:44 PM ]

Attaching a 2nd patch which properly updates the latch count in the unit test.

Comment by David Nolen [ 26/Aug/15 10:37 PM ]

This ticket does not explain why toString would get munged - it's not on the reserved list.

Comment by Mike Fikes [ 26/Aug/15 11:04 PM ]

You are right, David. The root cause actually has to do with (js-reserved? "toString") returning true when it should not do so. I suspect this has to do with the use of gobject/containsKey giving a false positive for things in the Object prototype, viz:

cljs.user=> (require '[goog.object :as gobject])
nil
cljs.user=> (gobject/containsKey #js {} "toString")
true

So, I retract my patch which completely glosses over the aspect that js-reserved? is probably the culprit.

Comment by Mike Fikes [ 26/Aug/15 11:53 PM ]

David, based on your feedback, attached a CLJS-1430-3.patch which might be closer to a correct approach in that it fundamentally revises js-reserved? to not return true for things like "toString".

My JavaScript is not strong enough for a feel as to whether the actual implementation in the patch is up to muster, nor whether munging is in the critical path for perf.

Comment by David Nolen [ 27/Aug/15 6:27 AM ]

Ah ok, revise the patch to remove the two tests, instead just use (.hasOwnProperty js-reserved foo)

Comment by Mike Fikes [ 27/Aug/15 7:56 AM ]

CLJS-1430-4.patch updated to use .hasOwnProperty, and ran all tests except Nashorn. Also ran self-host tests.

Comment by David Nolen [ 27/Aug/15 8:23 AM ]

fixed https://github.com/clojure/clojurescript/commit/8b7177a579d52a146d19902a19aa92708a567983





[CLJS-1422] cljs.js/eval-str fails for ns form on node.js with simple optimizations Created: 14/Aug/15  Updated: 27/Aug/15

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

Type: Defect Priority: Major
Reporter: Michael Ballantyne Assignee: Michael Ballantyne
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File CLJS_1422.patch     Text File CLJS_1422-v2.patch    
Patch: Code and Test
Approval: Accepted

 Description   

find-ns-obj has a special case for node.js under simple optimizations that uses eval to find the namespace object. If the object does not already exist this produces an exception; on other platforms find-ns-obj returns nil for a non-existant namespace.

As a result eval-str produces an error when evaluating a ns form for a new namespace.

There's a commented out test for this already in the source at https://github.com/clojure/clojurescript/blob/master/src/test/self/self_host/test.cljs#L121



 Comments   
Comment by Michael Ballantyne [ 14/Aug/15 1:17 PM ]

Added a patch with a fix and uncomment the existing test.

The special case of find-ns-obj for node.js under optimizations now checks each path segment with an eval of typeof to see if it exists, returning nil if the namespace object is absent.

Comment by David Nolen [ 26/Aug/15 10:47 PM ]

The overall idea is sound but the patch needs work. Just use goog.isObject instead of the needless js/eval call.

Comment by Michael Ballantyne [ 27/Aug/15 12:36 AM ]

I believe we need at least one `js/eval` of `typeof` in order to establish whether the object for the initial namespace segment exists, as there is no parent object to examine and referencing an undefined variable throws `ReferenceError`. `goog.isObject(thing)` where `thing` is a reference to an undefined variable throws `ReferenceError`; `typeof thing` returns `"undefined"`.

However, after obtaining the initial namespace segment we can use the normal `find-ns-obj*`. I've attached an updated patch using this strategy.

Comment by David Nolen [ 27/Aug/15 6:33 AM ]

Just a small code review thing to clarify the intent, why not just remove the multiple js/evals and lean on try/catch? The only possible error is attempting to use an undefined var which in that case we just want to bail anyway.





[CLJS-1353] range inconsistent with Clojure if step is 0 Created: 20/Jul/15  Updated: 26/Aug/15  Resolved: 26/Aug/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3308
Fix Version/s: None

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

Attachments: Text File CLJS-1353-14-Aug-15.patch     Text File CLJS-1353-15-Aug-15.patch    
Patch: Code and Test

 Description   
cljs.user=> (range 3 9 0)
()

With CLJ-1018 Clojure behaves this way and docstring for Clojure updated:

user=> (take 10 (range 3 9 0))
(3 3 3 3 3 3 3 3 3 3)


 Comments   
Comment by Samuel Miller [ 14/Aug/15 9:57 PM ]

Uploaded potential patch with updated tests.

Clojures implementation can be seen here...

https://github.com/clojure/clojure/blob/41af6b24dd5be8bd62dc2b463bc53b55e18cd1e5/src/jvm/clojure/lang/Range.java#L89

Basically what is boils down to is as long as the start and end are not equal give back the start number if the step is 0 (effectively repeat). Tried to keep the previous code for range the same and added a case for zero that checks for the equality of start and end.

Ran tests on Linux against SpiderMonkey, V8, and Nashorn they passed. Played around in the repl and compared against Clojure and seems to be equivalent.

Comment by Mike Fikes [ 14/Aug/15 11:51 PM ]

With CLJS-1353-14-Aug-15.patch, tests pass for me on OS X for V8, SpiderMonkey, and JavaScriptCore.

I also tried it in bootstrapped ClojureScript in JavaScriptCore on OS X, manually running the tests successfully.

Thinking about perf, it looks like it doesn't really affect the (probably common) code path for positive steps, and only adds one more necessary check to distinguish between negative and zero (which wasn't being done before). In the zero-step branch (which is probably not common at all), there is perhaps a tiny benefit in using == over = since we can assume numeric arguments. (You end up with a direct start === end as opposed to cljs.core._EQ_ being applied.)

There is one other bit of code that employs that pattern: https://github.com/clojure/clojurescript/blob/r1.7.48/src/main/cljs/cljs/core.cljs#L5058

Comment by David Nolen [ 15/Aug/15 5:22 PM ]

Yes the test should be changed to ==. Thanks.

Comment by Samuel Miller [ 15/Aug/15 11:19 PM ]

Thanks for the reviews. I updated the patch to use == instead of =

Comment by David Nolen [ 26/Aug/15 10:41 PM ]

fixed https://github.com/clojure/clojurescript/commit/bbcadde11fa5941fda804faa8fc92a5f33a242cf





[CLJS-1431] load-file doc output missing arglists Created: 21/Aug/15  Updated: 26/Aug/15  Resolved: 26/Aug/15

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

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: newbie
Environment:

noderepljs


Attachments: Text File CLJS-1431-8-21-15.patch    
Patch: Code

 Description   

The output for `(doc load-file)` is missing the arglists:

$ script/noderepljs 
ClojureScript Node.js REPL server listening on 52087
To quit, type: :cljs/quit
cljs.user=> (doc load-file)
-------------------------
load-file
REPL Special Function
  Sequentially read and evaluate the set of forms contained in the file.

nil

A line reading ([name]) should appear between load-file and REPL Special Function



 Comments   
Comment by Mike Fikes [ 21/Aug/15 7:50 PM ]

Root cause is the use of :arglist instead of :arglists here:
https://github.com/clojure/clojurescript/blob/v1.7/src/main/clojure/cljs/repl.cljc#L1113

Comment by Chris Pickard [ 21/Aug/15 8:44 PM ]

I've attached a patch (CLJS-1431-8-21-15.patch) that address the issue Mike reported

Comment by Mike Fikes [ 21/Aug/15 9:00 PM ]

For me, Chris Pickard's CLJS-1431-8-21-15.patch applies cleanly to master, produces the correct behavior in script/noderepljs, and unit tests pass for V8, SpiderMonkey, and JavaScriptCore (Nashorn skipped).

Comment by David Nolen [ 26/Aug/15 10:31 PM ]

fixed https://github.com/clojure/clojurescript/commit/19bcf10ccedf63f0e3bf0abfb7481f9c347895fc





[CLJS-1433] self-host: cljs.js/*eval-fn* passed nil :cache Created: 24/Aug/15  Updated: 26/Aug/15  Resolved: 26/Aug/15

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

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: bootstrap

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

 Description   

If you load some ClojureScript source that implements a namespace, the cljs.js/*eval-fn* will always be passed a :cache value of nil



 Comments   
Comment by Mike Fikes [ 24/Aug/15 12:24 PM ]

Simple defect—simply need to deref the atom.

Comment by David Nolen [ 26/Aug/15 10:28 PM ]

fixed https://github.com/clojure/clojurescript/commit/5f3c494ce4274177e8e9cb71adcb87b5f9394423





[CLJS-1403] Add script/bootstrap capability for Windows Created: 08/Aug/15  Updated: 26/Aug/15  Resolved: 26/Aug/15

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

Type: Enhancement Priority: Major
Reporter: Peter Stephens Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None
Environment:

All versions of Windows


Attachments: Text File cljs-1403-v2.patch     Text File cljs-1403-v3.patch    

 Description   

Implement a Windows shell script to download and unpack the correct versions of various ClojureScript dependencies.

Proposal: Implement a PowerShell script to do this work. PowerShell has been pre-installed on Windows since version 7 and is available (in various flavors) on Windows down to XP.

1. Downloading of files from the web is a proper command (Invoke-RestMethod) in PowerShell since version 3. Earlier versions of PowerShell could be supported by using Dotnet's WebClient.DownloadFile method.

2. Unzipping files is a little more difficult. But the GUI shell's capability is exposed through COM and this appears to work well from PowerShell.

3. The current dependency versions can be extracted from the /bin/sh script using regex.

See https://gist.github.com/pstephens/b260b1ad8b166489b562 for a rough draft of this script.



 Comments   
Comment by Peter Stephens [ 08/Aug/15 1:15 PM ]

Related issues: CLJS-24 and CLJS-66, both closed.

Comment by Peter Stephens [ 08/Aug/15 9:05 PM ]

Almost have a patch ready to submit... but lot's of the Windows shell wrappers are also broken. Working through the problems so I can hopefully get through the test suite on Windows.

Comment by David Nolen [ 09/Aug/15 10:25 AM ]

Thanks for working on this!

Comment by Peter Stephens [ 11/Aug/15 9:28 PM ]

This patch includes some basic Windows support scripts.
1. A port of script/bootstrap to PowerShell
2. A port of script/test to PowerShell
3. Updated bin/cljsc.bat to match bin/cljsc
4. And a minor tweak to bin/cljsc.clj to write exceptions to stderr and describe the nature of the exception.

Comment by Peter Stephens [ 11/Aug/15 9:46 PM ]

Tested on Windows 10 w/fresh clojurescript clone.

Bootstrap pulled down required dependencies.

Ran the test suite through Nashorn with two (apparently known) failing tests:
FAIL in (test-919-generic-cas) (at <anonymous> (builds/out-adv/core-advanced-test.js:NaN:963)
testing CLJS-919, CAS should on custom atom types
expected: (== (clojure.core/deref a0) 10)
actual: (not (== nil 10))

FAIL in (test-919-generic-cas) (at <anonymous> (builds/out-adv/core-advanced-test.js:NaN:963)
testing CLJS-919, CAS should on custom atom types
expected: (== (clojure.core/deref a1) 20)
actual: (not (== nil 20))

I couldn't find a Jira issue describing these failures but some work was done by Sebastian Bensusan on this: http://www.raynes.me/logs/irc.freenode.net/clojurescript/2015-04-20.txt and https://gist.github.com/bensu/c6302a33e56d5d7eae9e

Comment by David Nolen [ 12/Aug/15 6:49 AM ]

Patch looks good but I could not apply with git am. Did you following the patch instructions here https://github.com/clojure/clojurescript/wiki/Patches?

Also please submit only squashed patches. Thanks!

Comment by Peter Stephens [ 12/Aug/15 9:18 PM ]

Apparently there are encoding and and line ending issues from Windows generated patches using the linked patching instructions. I'll squash the commits and work out the encoding issues. Then I'll test-apply the patch to a fresh clone of master before resubmitting.

Comment by Peter Stephens [ 16/Aug/15 9:59 PM ]

1. Squished the patch. There is now only one commit.
2. Created the patch with Windows git bash shell. So the encoding is in proper ASCII without extra fluff.

NOTE: The batch and PowerShell files are encoded with CRLF... so special care needs to be taken when applying this patch. Here is the command I used:

git am --keep-cr --whitespace=nowarn < cljs-1403-v3.patch

The patch will not apply without keep-cr because the CR characters will be stripped out. The whitespace=nowarn option suppresses warnings but the patch will still apply without this option.

Comment by David Nolen [ 26/Aug/15 10:24 PM ]

fixed https://github.com/clojure/clojurescript/commit/d7740694be49ba38c778deae59a3e047ca881631





[CLJS-1330] self-host: .toString on int needs parens Created: 08/Jul/15  Updated: 18/Aug/15

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

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

https://github.com/swannodette/cljs-bootstrap



 Description   
cljs-bootstrap.core=> (with-out-str
      (c/emit
        (ensure
          (ana/analyze
            (assoc (ana/empty-env) :context :expr)
            '(.toString 1)))))) 
"1.toString$()"
  • Note the lack of parens CLJS-715
  • Note the dollar sign after fn name is covered by separate ticket CLJS-1430





[CLJS-1074] Externs inference Created: 02/Mar/15  Updated: 18/Aug/15

Status: In Progress
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3211
Fix Version/s: GSoC

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: Maria Geller
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Given all externs generally need to be supplied for js/foo we could probably automatically compute externs based on js/foo usage in user code. For this to work correctly we need to account for property access through js/foo i.e. (def Bar js/foo.Bar). This should infer that Bar is also a foreign object. Things gets more complicated for higher order cases, we probably want to support a ^js type hint.

Finally externs inference needs to account for externs likely already supplied by the user - i.e. don't emit dupes, Google Closure will complain.






[CLJS-1314] Node REPL can't load converted JS modules Created: 17/Jun/15  Updated: 17/Aug/15  Resolved: 17/Aug/15

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

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Maria Geller
Resolution: Completed Votes: 0
Labels: None
Environment:

Node REPL with the latest CommonJS loading impl


Attachments: Text File 0001-Illustration-of-fix.patch    

 Description   

I've been trying the CommonJS support and have been failing to get it to work in Node, and likewise so has Maria Neise. This is in the case of invoking build prior to launching the REPL as well as trying a new suggestion in CLJS-1313.

I cranked up the verbosity to see what is going on and ultimately hit upon the idea that

goog.require('module$libs$german');

isn't going to work in Node and instead needs the same alternative logic that is employed for :foreign-libs in cljs.compiler/load-libs, in particular this needs to be emitted.

cljs.core.load_file("out/german.js");

I hacked the code a bit and got the "german" CommonJS module to load in Node and be useable:

To quit, type: :cljs/quit
cljs.user=> (require '[german :refer [hello]])
Compiling out/cljs/core.cljs
Using cached cljs.core out/cljs/core.cljs
Compiling out/cljs/core.cljs
Using cached cljs.core out/cljs/core.cljs
goog.provide('cljs.user');
goog.require('cljs.core');
goog.require('cljs.repl');
goog.require('cljs.pprint');
cljs.core.load_file("out/german.js");

nil
cljs.user=> (hello)
module$libs$german.hello.call(null)
"Hallo"

The attached patch illustrates the hack to get the above to work. The real fix would be a generalization of this.



 Comments   
Comment by Mike Fikes [ 17/Jun/15 8:44 PM ]

(Sorry for the overly generalized ticket title; it should probably be weakened to just talk about foreign libs that have been converted to native libs.)

Comment by Mike Fikes [ 17/Jun/15 8:59 PM ]

Reproduction steps (note all of this is also in a repo at https://github.com/mfikes/test-commonjs ):

Set up a node_repl.clj:

(require 'cljs.repl)
(require 'cljs.build.api)
(require 'cljs.repl.node)

(def foreign-libs [{:file "libs/greeting.js"
       :provides ["greeting"]
       :module-type :commonjs}
      {:file "libs/german.js"
       :provides ["german"]
       :module-type :commonjs}])

(cljs.build.api/build "src"
  {:main 'foo.bar
   :output-to "out/main.js"
   :verbose true
   :foreign-libs foreign-libs})

(cljs.repl/repl (cljs.repl.node/repl-env)
  :watch "src"
  :output-dir "out"
  :foreign-libs foreign-libs)

Make a libs directory with german.js:

exports.hello = function() {
    return "Hallo";
};

and greeting.js:

var german = require("german");

exports.hello = function(name) {
    return german.hello() + ", " + name;
};

create a src/foo/bar.cljs with

(ns foo.bar
  (:require [greeting :refer [hello]]))

(enable-console-print!)

(println (hello "Welt!"))

Make a QuickStart cljs.jar uberjar and place it at root of tree and then

rlwrap java -cp cljs.jar:src clojure.main node_repl.clj

followed by

(require 'foo.bar)

You should see:

../out/foo/bar.js:6
cljs.core.println.call(null,module$libs$greeting.hello.call(null,"Welt!"));
                                                      ^
TypeError: Cannot read property 'call' of undefined
    at Object.<anonymous> (/Users/mfikes/Projects/test-commonjs/out/foo/bar.js:6:55)
    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 Module.require (module.js:365:17)
    at require (module.js:384:17)
    at global.CLOSURE_IMPORT_SCRIPT (repl:75:16)
    at Object.goog.require (repl:19:60)
    at repl:5:6
Comment by Mike Fikes [ 17/Jun/15 9:29 PM ]

Another comment: My "hack" works, but to be honest, I don't appreciate why the code doesn't work without the hack. And I couldn't defend it if you asked me to. Node can evidently load Closure modules that were produced by CLJS, so why would they fail for Closure modules produced by CommonJS processing? I bet there is more behind this and my hack may be papering over some deeper more fundamental problem.

For reference, in Node, my :js-dependency-index has

{nil {:requires [], :provides ["module$libs$german"], :url #object[java.net.URL 0x37d3d232 "file:/Users/mfikes/Projects/test-commonjs/out/german.js"], :closure-lib true, :lib-path "out/german.js"}

while in Ambly, the same has

{nil {:requires [], :provides ["module$libs$german"], :url #object[java.net.URL 0x34652065 "file:/Volumes/Ambly-81C53995/german.js"], :closure-lib true, :lib-path "/Volumes/Ambly-81C53995/german.js"}

(To clarfy, the above two :js-dependency-index bits are only part of the index... there is also all of the normal goog stuff.)

Comment by Maria Geller [ 12/Aug/15 6:15 PM ]

We are making progress with this one. A PR has just been merged into the Google Closure compiler that fixes the problem for CommonJS and AMD modules: https://github.com/google/closure-compiler/pull/1071. We will need to make a second PR for ECMAScript 6.

The reason that this failed before is, that when the JS modules are converted to Google Closure modules the new namespaces are declared as local, which is different from "normal" Google Closure modules. Since the Node REPL runs scripts in a separate context, the namespace functionality will not be seen outside this context.

Comment by Maria Geller [ 17/Aug/15 1:48 PM ]

The fix for converted ES6 modules has been merged into the Google Closure compiler: https://github.com/google/closure-compiler/pull/1081.

This should work now for all three module types. I think, this can be closed

Comment by David Nolen [ 17/Aug/15 6:07 PM ]

fixed





[CLJS-1429] self-host: Load dependent namespaces when client calls back with :cache Created: 17/Aug/15  Updated: 17/Aug/15  Resolved: 17/Aug/15

Status: Closed
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: Not Reproducible Votes: 0
Labels: bootstrap


 Description   

If cljs.js/*load-fn*'s callback is called with :cache, load dependent namespaces. Otherwise, the cached JavaScript will be loaded but not its dependencies.



 Comments   
Comment by Mike Fikes [ 17/Aug/15 9:45 AM ]

FWIW, I've worked around this downstream by making use of Google Closure's dependency management to actually do the load along with dependent namespaces, combined with simply returning an empty string for the source: https://github.com/mfikes/planck/blob/5dcc56c28732e79436fd9cc37dfd6cd42e0d076c/planck-cljs/src/planck/repl.cljs#L314

Comment by Mike Fikes [ 17/Aug/15 9:55 AM ]

I apologize for the ticket noise. This issue is invalid; it actually works.





[CLJS-1426] Add a cljs.core/IReader protocol definition Created: 16/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: Major
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

There is a cljs.core/IWriter

https://github.com/clojure/clojurescript/blob/a741cd4a3d0683f9dcfd08822343abcfa477d951/src/main/cljs/cljs/core.cljs#L618

The addition of cljs.core/IReader would be useful to projects that wish abstract over the concept of reading.

Perhaps a definition along the following lines:

(defprotocol IReader
  "Protocol for reading."
  (-read [this] "Returns available characters as a string or nil if EOF."))





[CLJS-1427] Add a cljs.core/ICloseable protocol definition Created: 16/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: Major
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Add protocol definition that abstracts over the concept of entities that can be closed.

Rationale: It naturally goes with IWriter and the proposed IReader (CLJS-1426), making it easy to define closable writers or readers.

(defprotocol IClosable
  "Protocol for closable entities."
  (-close [this] "Closes this entity."))





[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-1425] self-host: cljs.js/eval cb argument inconsistent with docstring Created: 14/Aug/15  Updated: 14/Aug/15

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

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


 Description   

The docstring for cljs.js/eval indicates a map (containing either an :error or :value key).

This is the case for :error: Here is an example:

https://github.com/clojure/clojurescript/blob/r1.7.48/src/main/cljs/cljs/js.cljs#L489

But for :value the value is returned without being wrapped in a map; instead the result of applying *eval-fn* is directly returned (and *eval-fn* is supposed to return the value). Here is an example:

https://github.com/clojure/clojurescript/blob/r1.7.48/src/main/cljs/cljs/js.cljs#L499






[CLJS-1420] get-in behavior differs from Clojure by always deferring to the 3 arity fn Created: 12/Aug/15  Updated: 14/Aug/15

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

Type: Defect Priority: Minor
Reporter: Kevin Webster Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File cljs_1420.patch    

 Description   

CLJS-464 should be reverted since the commit changes how get-in is handled by types that implement ILookup.

Since get-in always defers to the 3 arity function, the 2 arity ILookup -lookup fn never gets called. This behavior is inconsistent with how Clojure behaves.

clj:

(get-in
  (reify
    clojure.lang.ILookup
    (valAt [o k] :2-arity)
    (valAt [o k not-found] :3-arity))
  [:foo])
=> :2-arity

cljs:

(get-in
  (reify
    ILookup
    (-lookup [o k] :2-arity)
    (-lookup [o k not-found] :3-arity))
  [:foo])
=> :3-arity


 Comments   
Comment by Kevin Webster [ 12/Aug/15 3:30 PM ]

Tested patch in both V8 & Nashorn

Comment by David Nolen [ 14/Aug/15 7:51 PM ]

Please modify the patch to include a test case. The patch should be squashed into a single commit.





[CLJS-1312] JS module support: Properly convert UMD pattern Created: 15/Jun/15  Updated: 14/Aug/15  Due: 21/Aug/15  Resolved: 14/Aug/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: GSoC
Fix Version/s: GSoC

Type: Enhancement Priority: Major
Reporter: Maria Geller Assignee: Maria Geller
Resolution: Completed Votes: 1
Labels: None


 Description   

Many popular libraries use the following or a similar idiom:

if (typeof define == "function" && define.amd) {
    define(function() {
        return greeting;
    });
} else if (typeof module != "undefined" &&
           module.exports) {
    module.exports = greeting;
} else {
    window["greeting"] = greeting;
}

The ProcessCommonJSModules class converts this to the following:

if (typeof define == "function" && define.amd) {
    define(function() {
        return greeting;
    });
} else {
    if (typeof module != "undefined" && module$greeting) {
        module$greeting = greeting;
    } else {
        window["greeting"] = greeting;
    }
}

module will not be defined, so the greeting object will not be assigned to the new module namespace. We need to check with the Google Closure compiler mailing list if we can submit a patch that replaces module so that the condition succeeds.



 Comments   
Comment by Maria Geller [ 15/Jun/15 1:35 PM ]

Asked about this on Google Closure compiler mailing list: https://groups.google.com/forum/#!topic/closure-compiler-discuss/-M1HBUn35fs

Comment by Maria Geller [ 22/Jul/15 8:52 PM ]

PR: https://github.com/google/closure-compiler/pull/1048

Comment by Maria Geller [ 13/Aug/15 1:49 PM ]

This has been merged into the Google Closure compiler and seems to work: https://github.com/google/closure-compiler/commit/aa0a99cf380b05b2185156735d023b6fa78ec4ac

Should be good to close now





[CLJS-1406] macroexpand munging goog.string.StringBuffer Created: 09/Aug/15  Updated: 14/Aug/15

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

Type: Defect Priority: Minor
Reporter: Matthew Molloy Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bootstrap
Environment:

Bootstrapped Planck REPL


Attachments: Text File cljs_1406.patch     Text File cljs_1406_test.patch    

 Description   

(macroexpand '(with-out-str)) on bootstrapped ClojureScript gives (let* [sb__13582__auto__ (string.StringBuffer.)]...
on JVM compiled ClojureScript it is (let* [sb__1155__auto__ (goog.string.StringBuffer.)]...

goog.string.StringBuffer has been incorrectly munged to string.StringBuffer when the cljs.js compiler is used.



 Comments   
Comment by Joel Martin [ 13/Aug/15 7:28 PM ]

I ran into this today. Here is a test case for the self host tests that reproduces the problem.

Comment by Joel Martin [ 13/Aug/15 7:50 PM ]

Fix the test. Propose a fix that just puts "js/" in front to prevent the broken name resolution. Probably not the correct solution, but it does work.

Comment by David Nolen [ 14/Aug/15 7:50 PM ]

Not going to take the current patches. The underlying resolution issue needs to be fixed.





[CLJS-1424] (def) or (defn) will throw an obscure exception Created: 14/Aug/15  Updated: 14/Aug/15

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

Type: Defect Priority: Minor
Reporter: Sean Grove Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: cljs


 Description   

Simply trying to compile `(def)` will throw an exception: "Caused by: clojure.lang.ArityException: Wrong number of args (1) passed to: analyzer/eval1440/fn-1441/pfn-1442"

While obviously not a valid construct, the error could be significantly more descriptive, and also show the line number where it happened.

A similar issue happens with `(defn)`: "Caused by: clojure.lang.ExceptionInfo: Wrong number of args (2) passed to: core/defn--12230"






[CLJS-1423] self-host: Requiring analyzer/compiler breaks unchecked Boolean Created: 14/Aug/15  Updated: 14/Aug/15

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

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

Quick Start



 Description   

Grab the Quick Start cljs.jar, and make a src/foo/core.cljs file with

(ns foo.core)

(if 0 
  (prn 1) 
  (prn 2))

Build it using java -cp cljs.jar:src clojure.main build.clj where build.clj contains:

(require 'cljs.build.api)

(cljs.build.api/build "src" 
  {})

Inspect out/foo/core.js:

// Compiled by ClojureScript 1.7.48 {}
goog.provide('foo.core');
goog.require('cljs.core');
if(cljs.core.truth_((0))){
cljs.core.prn.call(null,(1));
} else {
cljs.core.prn.call(null,(2));
}

//# sourceMappingURL=core.js.map

Now revise src/foo/core.cljs to require the analyzer namespace:

(ns foo.core
  (:require cljs.analyzer))

(if 0 
  (prn 1) 
  (prn 2))

Build again and inspect out/foo/core.js:

// Compiled by ClojureScript 1.7.48 {}
goog.provide('foo.core');
goog.require('cljs.core');
goog.require('cljs.analyzer');
if((0)){
cljs.core.prn.call(null,(1));
} else {
cljs.core.prn.call(null,(2));
}

//# sourceMappingURL=core.js.map

Note the lack of cljs.core.truth_



 Comments   
Comment by Mike Fikes [ 14/Aug/15 3:41 PM ]

Speculation: The intent of this line

https://github.com/clojure/clojurescript/blob/r1.7.48/src/main/clojure/cljs/analyzer.cljc#L1565

is to provide *unchecked-if* capability in bootstrapped environments. But, perhaps it additionally acts as a meta-circular gateway allowing cljs.analyzer/*unchecked-if* to be set in new scenarios while compiling ClojureScript using the JVM-based compiler. (That line was not there previously.)

Fact: If you remove this line, it works. Additionally, if you revise it to set a literal false, things work. Neither are satisfying as they would defeat the original intent of that bit of code.

I have a much more complicated downstream usage of bootstrap that exhibits similar behavior with respect to the minimal repro above (https://github.com/mfikes/planck/issues/84).





[CLJS-1419] enhance numeric inference, if + number? test on local var should tag local var in the successful branch Created: 12/Aug/15  Updated: 12/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1297] defrecord does not emit IKVReduce protocol Created: 03/Jun/15  Updated: 11/Aug/15

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

Type: Defect Priority: Major
Reporter: Daniel Skarda Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: newbie

Attachments: Text File CLJS-1297-19-July-2015.patch    

 Description   

Records are maps and in Clojure they support reduce-kv (IKVReduce protocol).
This is not true in ClojureScript:

(defrecord Foobar [x y])
 (reduce-kv assoc {} (Foobar. 1 2))

Fails wit Error: No protocol method IKVReduce.-kv-reduce defined for type : [object Object]



 Comments   
Comment by David Nolen [ 03/Jun/15 7:25 PM ]

Just seems like an oversight. Patch welcome, this one is a relatively easy one.

Comment by Daniel Skarda [ 04/Jun/15 2:53 AM ]

OK

I checked Clojure implementation. Records do not implement any reduce protocol on their own. For IKVReduce records use default implementation using reduce and destructuring. Is this approach OK?

Recently Alex Miller implemented many optimizations of reduce protocols in Clojure. Eg range returns an object which implements IReduce protocol so reduce (and transducers in general) can take advantage of it. Any plans for such optimizations in ClojureScript?

;;clojure/src/clj/clojure/core.clj:6523
;;slow path default
clojure.lang.IPersistentMap
(kv-reduce 
  [amap f init]
  (reduce (fn [ret [k v]] (f ret k v)) init amap))
Comment by David Nolen [ 04/Jun/15 9:05 AM ]

Going with the Clojure implementation is fine. Yes all of the optimizations in 1.7.0 are on the table for ClojureScript but these are separate issues from this one.

Comment by Samuel Miller [ 16/Jul/15 10:39 PM ]

Mind if I take this as my first cljs bug? Poking around quickly I think I know what needs to happen.

Comment by David Nolen [ 17/Jul/15 5:21 AM ]

Sure! Have you submitted your CA yet?

Comment by Samuel Miller [ 17/Jul/15 7:13 PM ]

Yes, I did yesterday.

Comment by Samuel Miller [ 20/Jul/15 9:52 PM ]

Here is a potential patch. I implemented a basic IKVreduce based on Daniel Skarda's comment. Note: I am a little fuzzy on macros still so please look over what I have. There is probably a better way. Also added a test for reduce-kv on records.

I ran the test on Linux on V8 and SpiderMonkey. I plan to get JSC and Nashorn working and tested this week but if someone wants to test them out before that would be great.

Comment by Sebastian Bensusan [ 23/Jul/15 6:45 PM ]

Experience report:

I just tested the patch in the Node Repl and it seems to work:

cljs.user=> (defrecord A [a b])
cljs.user/A
cljs.user=> (reduce-kv (fn [m k v] (assoc m k (inc v))) {} (A. 1 2))
{:a 2, :b 3}

and the provided tests passed in Spidermonkey, V8, and Nashorn (I don't have JSC installed).

For completeness: before applying the patch the same code fails with:

Error: No protocol method IKVReduce.-kv-reduce defined for type : [object Object]
Comment by David Nolen [ 10/Aug/15 10:22 PM ]

Is this the same approach taken by Clojure?

Comment by Samuel Miller [ 10/Aug/15 10:36 PM ]

You can see the relevant current Clojure code here...
https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L6526
I think it is the same. I literally just tried to translate it over into CLJS. I might of understood something wrong though.

Comment by David Nolen [ 11/Aug/15 6:10 AM ]

Yes that's the slow path. Please use the implementation used by defrecord instead. If defrecord doesn't have one then this patch is OK.

Comment by Samuel Miller [ 11/Aug/15 8:48 PM ]

As far as I can tell there is no implementation on defrecord itself however there are separate implementations on the the java classes PersistentVector, PersistentArrayMap, PersistentTreeMap, and PersistenHashMap in pure java. I am not sure if you would want to do something similar for Clojurescript.

I can also spend some time trying to make a more performant version.





[CLJS-1379] self-host: Macroexpansion defeated when calling helper Created: 29/Jul/15  Updated: 11/Aug/15  Resolved: 11/Aug/15

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

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: bootstrap
Environment:

1.7.10



 Description   

If you have a namespace:

(ns foo.macros
  (:require foo.helper))

(defmacro my-inc [n]
  `(foo.helper/my-inc ~n))

That makes use of a helper namespace:

(ns foo.helper)

(defn my-inc [x]
  (inc x))

Then things will work properly for non-bootstrapped ClojureScript. Here is an example use in Ambly:

cljs.user=> (require 'foo.helper)
nil
cljs.user=> (require-macros 'foo.macros)
nil
cljs.user=> (defn f [] (foo.macros/my-inc 3))
#'cljs.user/f
cljs.user=> f
#<function cljs$user$f() {
return foo.helper.my_inc.call(null,(3));
}>
cljs.user=> (f)
4

But, the same sequence in bootstrapped ClojureScript fails:

cljs.user=> (require 'foo.helper)
nil
cljs.user=> (require-macros 'foo.macros)
nil
cljs.user=> (defn f [] (foo.macros/my-inc 3))
#'cljs.user/f
cljs.user=> f
#<function cljs$user$f() {
return foo.macros.my_inc.call(null,3);
}>
cljs.user=> (f)
Error occurred
cljs$user$f

Note that the emitted JavaScript in this second case treats the macro as a function.

Also, I've seen that if you eliminate the call to a helper, but instead, say make direct use of say, inc, then macro expansion works properly in bootstrapped ClojureScript (and for the example above you end up with JavaScript code that adds 1 and 3.)

Of course, these are with downstream REPLs. Let me know if a minimal repro without any downstream stuff is desired and I'm sure I can scratch one together involving the same calls Planck is making to cljs.js.



 Comments   
Comment by David Nolen [ 30/Jul/15 3:18 PM ]

The second case is only possible if for some reason the macros could not get loaded.

Comment by Mike Fikes [ 30/Jul/15 5:12 PM ]

As discussed in IRC, Mike to add minimal repro to this ticket.

Comment by David Nolen [ 10/Aug/15 10:09 PM ]

If we can't get a reproducer for this will close soon

Comment by Mike Fikes [ 11/Aug/15 10:30 AM ]

The root cause is that cljs.tools.reader/resolve-symbol is not bound.

Adding the binding pair r/resolve-symbol ana/resolve-symbol to cljs.js/eval-str* resolves the issue, but the correct fix may involve binding this in other entry points as well.

Still working on trying to put together a good repro. An ideal place would be self-host.test test-eval-str-with-require, but something is preventing those tests from running under Node.js (it looks like the ns form that creates foo.bar derails because the outer foo object is not created. Interestingly, if I use JavaScriptCore (via Planck, pointed at clojurescript/src/test/self as its source directory), and I also rename helper.clj -> helper.cljc, I am able to make a lot of progress (repro the issue and have augmented test cases pass, with the right changes).

So, I think the key to progress on this one is to get these tests to run under Node.js. I'll see if I can get them to work.

Comment by David Nolen [ 11/Aug/15 4:03 PM ]

possible fix on master that needs testing.

Comment by Mike Fikes [ 11/Aug/15 4:53 PM ]

Confirmed fixed using downstream Planck REPL.

As an aside, even though regular ClojureScript REPLs (like Ambly) need the additional (require 'foo.helper) prior to (require-macros 'foo.macros) in order for this code to work, Planck (and probably all bootstrapped REPLs), can simply issue (require-macros 'foo.macros) and have the code work. (In other words, bootstrapped ClojureScript appears to be capable of loading the transitive dependency for this case.)

Comment by David Nolen [ 11/Aug/15 5:20 PM ]

fixed https://github.com/clojure/clojurescript/commit/2677ff7a595e872d9393237818894d84a0214083





[CLJS-1412] Add JSDoc type information to individual IFn methods Created: 10/Aug/15  Updated: 11/Aug/15

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

Type: Enhancement Priority: Major
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-1415] Handling JSDoc param name [x] optional syntax Created: 10/Aug/15  Updated: 11/Aug/15

Status: In Progress
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
Fix Version/s: Next

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: type-check





[CLJS-1402] Source Mapping Closure Error Logger Created: 08/Aug/15  Updated: 11/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: type-check


 Description   

Current error reports generated by Google Closure point back to the generated JavaScript sources. For JavaScript source that originated from ClojureScript we should generated source mapped reports.






[CLJS-1299] reader/read-string does not handle \space literal Created: 05/Jun/15  Updated: 11/Aug/15  Resolved: 11/Aug/15

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

Type: Defect Priority: Major
Reporter: Ray Huang Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: None
Environment:

OSX


Attachments: Text File CLJS1299.patch    

 Description   

I notice that in Clojure Script, it doesn't know how to interpret \space using reader/read-string

In Clojure:

entangle.single=> (pr-str {:foo \space})
    "{:foo \\space}"

Then, attempting to deserialize in ClojureScript:

entangle.client => (reader/read-string "{:foo \\space}")
    #<Error: Map literal must contain an even number of forms>
    Error: Map literal must contain an even number of forms
        at Function.cljs.reader.reader_error.cljs$core$IFn$_invoke$arity$variadic (http://localhost:10000/public/js/out/cljs/reader.js:156:8)
        at cljs$reader$reader_error (http://localhost:10000/public/js/out/cljs/reader.js:152:33)
        at cljs$reader$read_map (http://localhost:10000/public/js/out/cljs/reader.js:436:26)
        at cljs$reader$read (http://localhost:10000/public/js/out/cljs/reader.js:810:34)
        at cljs$reader$read_string (http://localhost:10000/public/js/out/cljs/reader.js:837:25)


 Comments   
Comment by Ray Huang [ 04/Jul/15 11:07 AM ]

I've created a patch to support these literals. This based on the JVM's `CharacterReader` class. I've added a test case as well and it passes all the tests.

Comment by David Nolen [ 08/Jul/15 11:57 AM ]

I reviewed the patch. Please use identical? not = for the char equality tests. Thanks.

Comment by Ray Huang [ 09/Jul/15 3:32 AM ]

Good catch. Thanks, I've attached the 2nd version with the additional fix.

Comment by David Nolen [ 10/Aug/15 10:25 PM ]

Mostly looks good but lets use property access for string length not alength which is only for arrays. Also the patch should be squashed.

Comment by Ray Huang [ 10/Aug/15 11:03 PM ]

Done! Rebased, squashed and ready to go.

Comment by David Nolen [ 11/Aug/15 6:14 AM ]

fixed https://github.com/clojure/clojurescript/commit/30b809bedd976e1d2e37f99cfa72a20abd4b9186





[CLJS-1287] CLJS exceptions should use same format as Clojure 1.7 (#exception {...}) Created: 25/May/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Defect Priority: Major
Reporter: Daniel Skarda Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: None


 Description   

Recent Clojure 1.7-RC adds new print format compatible with EDN syntax:

(ex-info "foobar" {:bar :baz})
#error {
 :cause "foobar"
 :data {:bar :baz}
 :via
 [{:type clojure.lang.ExceptionInfo
   :message "foobar"
   :data {:bar :baz}
   :at [clojure.core$ex_info invoke "core.clj" 4591]}]
 ...
}

(try (.indexOf 0 "baz") (catch Throwable e e))
#error {
 :cause "No matching method found: indexOf for class java.lang.Long"
 :via
 [{:type java.lang.IllegalArgumentException
   :message "No matching method found: indexOf for class java.lang.Long"
   :at [clojure.lang.Reflector invokeMatchingMethod "Reflector.java" 53]}]
 ...
}

Unfortunatelly ClojureScript format is different (and sometimes even not compatible with EDN):

(ex-info "foobar" {:bar :baz})
#ExceptionInfo{:message "foobar", :data {:bar :baz}}

(try (.indexOf 0  "baz") (catch js/Error e e))
#<TypeError: Object 0 has no method 'indexOf'>

The request to unify the output of exceptions might sound like "nitpicking".

However please consider applications, which share (lots of) code between server and client.
Any arbitrary difference between Clojure and ClojureScript adds future accidental complexity.

Hint: looking for green light and answer patches welcome

Last thing to consider is how to deal with stacktraces in the patch. Recent CLJS parses have a parser implemented on server side in Clojure. Should the patch move parsing to ClojureScript?



 Comments   
Comment by David Nolen [ 25/May/15 10:14 AM ]

Yes patch welcome. Thanks!

Comment by David Nolen [ 10/Aug/15 10:21 PM ]

this has been fixed in master with the 1.7 releases





[CLJS-1134] Lift protocols from cljs.closure into cljs.protocols ns Created: 17/Mar/15  Updated: 10/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1305] Node.js REPL load-file regression Created: 09/Jun/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: Unassigned
Resolution: Not Reproducible Votes: 0
Labels: None


 Description   

Appears to be the same as the one we encountered in browser REPL due to the Closure Library dep bump.



 Comments   
Comment by David Nolen [ 10/Aug/15 10:19 PM ]

cannot reproduce.





[CLJS-1190] definterface support Created: 03/Apr/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

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


 Description   

A interop thing for JSDoc using JavaScript libraries, but also a potential Clojure interop thing. We can generate an empty object + methods that includes JSDoc @interface annotation.



 Comments   
Comment by David Nolen [ 10/Aug/15 10:17 PM ]

there's enough support this without adding more sugar.





[CLJS-1417] cljs.js: require macros failure error message pertains to non-macros Created: 10/Aug/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None
Environment:

Shipping Node REPL


Attachments: Text File CLJS-1417.patch    

 Description   

Repro, via script/noderepljs:

(require 'cljs.js)

(binding [cljs.js/*load-fn* (fn [x cb] (prn x) (cb nil))]
 (cljs.js/require 'foo.bar {:macros-ns true} prn))

this prints

{:name foo.bar, :macros true, :path "foo/bar"}
{:error #error {:message "No such namespace: foo.bar, could not locate foo/bar.cljs, foo/bar.cljc, or Closure namespace \"foo.bar\"", :data {:tag :cljs/analysis-error}}}


 Comments   
Comment by Mike Fikes [ 10/Aug/15 2:07 PM ]

The attached patch adds a new error message which is careful to list the files off in the order specified here (as is done for the non-macros ns error): https://github.com/clojure/clojurescript/blob/v1.7/src/main/cljs/cljs/js.cljs#L61

This patch is effectively adding an error message that would only be used by bootstrapped environments. It could be further revised so that the same error message is emitted if require-macros is called in a regular ClojureScript REPL for a unlocatable namespace. (This patch doesn't revise that behavior, which results in Clojure complaining about __init.class and clj files, but not cljc---perhaps Clojure itself in the future will be revised to mention cljc.

Comment by Mike Fikes [ 10/Aug/15 2:17 PM ]

I created CLJ-1797 covering the 2nd paragraph in the previous comment about require-macros in non-bootstrapped ClojureScript REPLs.

Comment by David Nolen [ 10/Aug/15 10:14 PM ]

fixed https://github.com/clojure/clojurescript/commit/637e247dc57695b153f3deb27b9999f77c2fa015





[CLJS-1404] var resolution for @param and @return type Created: 08/Aug/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: None


 Description   

Adding typing information is tedious due to namespacing. We should resolve @param and @return to avoid the boilerplate.



 Comments   
Comment by David Nolen [ 10/Aug/15 10:10 PM ]

fixed https://github.com/clojure/clojurescript/commit/9b54d4db44ede59c928141f2a1e18ceaf640aa76





[CLJS-1416] cljs.util/last-modified leaks files Created: 10/Aug/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3308
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Erik Dannenberg Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: newbie
Environment:

windows 8.1



 Description   

Using boot[1] on Windows we run into "could not delete file.." errors as described in #CLJ-1659.

File leak detector[2] suggests the culprit is cljs.util/last-modified:

1 descriptors are open
#1 C:\Users\ed\.boot\cache\tmp\Users\ed\projekte\sente-system\5ho\-c4hx1l\adzerk\boot_reload.cljs by thread:clojure-agent-send-off-pool-0 on Mon Aug 10 17:54:39 CEST 2015
	at java.io.FileInputStream.<init>(FileInputStream.java:139)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:90)
	at sun.net.www.protocol.file.FileURLConnection.initializeHeaders(FileURLConnection.java:110)
	at sun.net.www.protocol.file.FileURLConnection.getLastModified(FileURLConnection.java:178)
	at cljs.util$last_modified.invokeStatic(util.cljc:136)
	at cljs.util$last_modified.invoke(util.cljc)
	at cljs.analyzer$requires_analysis_QMARK_.invokeStatic(analyzer.cljc:2179)
	at cljs.analyzer$requires_analysis_QMARK_.invoke(analyzer.cljc)
	at cljs.analyzer$requires_analysis_QMARK_.invokeStatic(analyzer.cljc:2164)
	at cljs.analyzer$requires_analysis_QMARK_.invoke(analyzer.cljc)
	at cljs.analyzer$analyze_file.invokeStatic(analyzer.cljc:2220)
	at cljs.analyzer$analyze_file.invoke(analyzer.cljc)
	at cljs.analyzer$analyze_deps.invokeStatic(analyzer.cljc:1291)
	at cljs.analyzer$analyze_deps.invoke(analyzer.cljc)
	at cljs.analyzer$eval1868$fn__1870.invoke(analyzer.cljc:1545)
	at clojure.lang.MultiFn.invoke(MultiFn.java:251)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:1900)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc)
	at cljs.analyzer$analyze$fn__2118.invoke(analyzer.cljc:1992)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:1985)
	at cljs.analyzer$analyze.invoke(analyzer.cljc)
	at cljs.compiler$compile_file_STAR_$fn__3216.invoke(compiler.cljc:1027)
	at cljs.compiler$with_core_cljs.invokeStatic(compiler.cljc:968)
	at cljs.compiler$with_core_cljs.invoke(compiler.cljc)
	at cljs.compiler$compile_file_STAR_.invokeStatic(compiler.cljc:988)
	at cljs.compiler$compile_file_STAR_.invoke(compiler.cljc)
	at cljs.compiler$compile_file$fn__3248.invoke(compiler.cljc:1129)
	at cljs.compiler$compile_file.invokeStatic(compiler.cljc:1109)
	at cljs.compiler$compile_file.invoke(compiler.cljc)
	at cljs.compiler$compile_root.invokeStatic(compiler.cljc:1181)
	at cljs.compiler$compile_root.invoke(compiler.cljc)
	at cljs.closure$compile_dir.invokeStatic(closure.clj:385)
	at cljs.closure$compile_dir.invoke(closure.clj)
	at cljs.closure$eval3614$fn__3615.invoke(closure.clj:425)
	at cljs.closure$eval3567$fn__3568$G__3558__3575.invoke(closure.clj:331)
	at cljs.closure$eval3627$fn__3628.invoke(closure.clj:439)
	at cljs.closure$eval3567$fn__3568$G__3558__3575.invoke(closure.clj:331)
	at adzerk.boot_cljs.impl.CljsSourcePaths$fn__4011.invoke(impl.clj:14)

Example app that should reproduce the error: https://github.com/danielsz/sente-boot

clone, run boot dev, change a source file => file exists errors

[1] http://boot-clj.com/
[2] http://file-leak-detector.kohsuke.org/



 Comments   
Comment by Alex Miller [ 10/Aug/15 1:52 PM ]

The patch at http://dev.clojure.org/jira/secure/attachment/14273/clj-1659-v3.patch for CLJ-1659 may be helpful in creating a patch.

Comment by David Nolen [ 10/Aug/15 10:07 PM ]

fixed https://github.com/clojure/clojurescript/commit/7f29513de3ace7a14df220f4d0be1a8faeb92723





[CLJS-868] no arity warnings on recursive calls Created: 03/Oct/14  Updated: 10/Aug/15

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

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


 Description   

If a function recursively invokes itself within its own body the invoke will not be checked for arity mismatch.



 Comments   
Comment by Samuel Miller [ 10/Aug/15 10:06 PM ]

Took some time to look at this issue. Originally thought "Do what loop/recur does" but that does not take into account multi-arity. It seems like maybe the best option is to somehow use the second pass of the analyze(analyze-fn-methods-pass2). The entire information about the function is present and the warning section of the code gets triggered but because of no-warn is ignored. Any other ideas for a solution to this?





[CLJS-1418] self-host: defprotocol regressed with arity error Created: 10/Aug/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: bootstrap

Attachments: Text File 0001-Exhibit-defprotocol-failure.patch     Text File CLJS-1481.patch    

 Description   

There is a regression in the ability to evaluate (defprotocol Foo) in master.

The attached patch exhibits the problem with a unit test that triggers the issue, causing the following to be emitted:

FAIL in (test-eval-str) (at /Users/mfikes/Projects/clojurescript/builds/out-self/core-self-test.js:10969:98)
expected: (nil? error)
  actual: (not (nil? #error {:message "Could not eval ", :data {:tag :cljs/analysis-error}, :cause #error {:message "Invalid arity: 4 at line 1 ", :data {:file nil, :line 1, :column 1, :tag :cljs/analysis-error}, :cause #object[Error Error: Invalid arity: 4]}}))


 Comments   
Comment by Mike Fikes [ 10/Aug/15 4:00 PM ]

Regression occurred with this commit: https://github.com/clojure/clojurescript/commit/d435b4395c719644a4a16f0277ae912f61db42a6

Comment by David Nolen [ 10/Aug/15 7:28 PM ]

fixed https://github.com/clojure/clojurescript/commit/04d0b92da513c8e7129e06bc34b19b1801207c32





[CLJS-1017] Support :main for :advanced & :simple builds Created: 06/Feb/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 1
Labels: None

Attachments: Text File 0001-Allow-main-entry-point-for-advanced-optimization.patch    

 Description   

This would use the supplied entry point to compute the build instead of looking at everything in the source directory.



 Comments   
Comment by Jonathon McKitrick [ 10/Jul/15 9:14 AM ]

Initial effort to solve the issue, but will need some cleanup.

Comment by Jonathon McKitrick [ 10/Jul/15 9:18 AM ]

Initial efforts with a simple fix resulted in the constants table merged in the wrong order, after the application code. After more detective work, I ran the initial file through the analyzer and then the dependency order sort, and this solved this issue.

Not all tests pass on master as of this writing, but this patch does not create additional breakage. After this patch is reviewed and cleaned up, I would expect to move on to writing the test case as well.

Comment by David Nolen [ 10/Jul/15 4:39 PM ]

Patch mostly looks good except for the ugliness around :output-file. I've changed cljs.closure/compile-file to also allow :output-to. Please change the logic to avoid specifying :output-file.

Also I see no failing tests on master. Can you clarify about that?

Comment by Jonathon McKitrick [ 10/Jul/15 7:47 PM ]

Updated patch

Comment by David Nolen [ 13/Jul/15 2:56 PM ]

So are you saying applying the patch causes the test to fail? If you've identified the issue modify the patch to solve the issue.

Comment by David Nolen [ 13/Jul/15 3:27 PM ]

Jonathan let's please stop talking about the unrelated failing tests. It's cluttering up this issue. I cannot reproduce these failures at all nor have I heard of problems from other developers testing and building from master.

Comment by Jonathon McKitrick [ 10/Aug/15 8:33 AM ]

David, do you need anything else from me on this ticket?

Comment by David Nolen [ 10/Aug/15 8:48 AM ]

Nope will take a look today.

Comment by David Nolen [ 10/Aug/15 4:39 PM ]

fixed https://github.com/clojure/clojurescript/commit/c90f4c169812ad35624493a10b2e3b919fe0a022

Comment by David Nolen [ 10/Aug/15 4:41 PM ]

Jonathan the patch included various unnecessary calls and didn't handle the :simple case. We could have gone back and forth another round but it was just faster for me to fix it in this case. Thanks for the bump and pushing this to get done.





[CLJS-1397] Honor :def-emits-var for def w/o init and declare Created: 06/Aug/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Not Reproducible Votes: 0
Labels: None

Approval: Vetted

 Description   

Currently :def-emits-var works only if there is an init form. (In particular, the change for CLJS-934 did not revise the when init aspect of emit* def.) This ticket asks that :def-emits-var be extended to cover this case as well.

Additionally, this ticket asks that the same be made to hold true for declare (if it is not already implicitly take care of the def without init case.

Rationale:

  1. It is justifiable in and of itself. (:def-emits-var should work unconditionally whether a def has an init).
  2. A consequence for normal REPL use would be that init-less def forms would work at the REPL and declare would work at the REPL without complaining.

The second issue above (REPL use) wouldn't be taken care of if :def-emits-var is explicitly turned off.



 Comments   
Comment by Mike Fikes [ 10/Aug/15 2:37 PM ]

No longer reproducible in master (for both def and declare), likely as a result of surrounding Google Closure type stuff: https://github.com/clojure/clojurescript/commit/5800cfba697c8c261d8a3ff4c22a49fb749a827b





[CLJS-1413] Why can't I access goog.math.Long.ONE? Created: 10/Aug/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Defect Priority: Minor
Reporter: Gary Fredericks Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: None


 Description   

Not sure if this is a bug or misuse, just creating this ticket so it doesn't get lost.

When I upgraded from 1.7.28 to 1.7.48 I had to stop referencing the ONE constant in the Long class (and use .fromNumber(1) as a workaround), and I don't know why. Forcing the old version of GCL fixes the problem. More details in this commit.



 Comments   
Comment by David Nolen [ 10/Aug/15 8:15 AM ]

We periodically bump Google Closure. Are you sure this constant has not been removed?

Comment by Gary Fredericks [ 10/Aug/15 1:32 PM ]

Yep, that's it. I had checked the commit history for that file but did not look closely enough. Removing the constant seemed like such an unlikely thing to happen, but apparently they got some worthwhile dead code removal stuff from it: https://github.com/google/closure-library/commit/f05646269a08e81810ae1b4f921f9a857049a33c.

Sorry for the noise.





[CLJS-1414] Only munge @param @return if type checking Created: 10/Aug/15  Updated: 10/Aug/15  Resolved: 10/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Comments   
Comment by David Nolen [ 10/Aug/15 12:34 PM ]

fixed https://github.com/clojure/clojurescript/commit/8ddfb80e27dee5ec68a534124288872a94624c22





[CLJS-1411] make-array signature differs from clojure Created: 10/Aug/15  Updated: 10/Aug/15

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

Type: Defect Priority: Minor
Reporter: Lars Andersen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: newbie


 Description   

When I was porting clj code using a two-dimensional array I noticed the cljs version only accepts size, instead of dim & more-dims.

Signature in cljs: ([size] [type size])
signature int clj: ([type len] [type dim & more-dims])



 Comments   
Comment by David Nolen [ 10/Aug/15 7:28 AM ]

Unlike on the JVM there's no efficient way to allocate multi-dimensional arrays. An enhancement patch that includes a modification to the docstring that warns about the computational cost is welcome.

Comment by Lars Andersen [ 10/Aug/15 8:00 AM ]

What I want is for the signatures to be the same, so I don't have to use reader conditionals to get the same behavior on the two platforms.

When I'm asking for a two-dimensional array, I'm presumably going to do work that is O(m*n), so even though the pre-allocation is O(m*n) too it won't affect the runtime of the program significantly.

Comment by David Nolen [ 10/Aug/15 8:13 AM ]

Lars, yes I understood As I said patch welcome. This is a very ClojureScript newbie friendly ticket.





[CLJS-1410] Support source maps in deps.cljs Created: 09/Aug/15  Updated: 09/Aug/15

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

Type: Enhancement Priority: Major
Reporter: Martin Klepsch Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None


 Description   

There should be support to package source maps with a foreign-lib using deps.cljs






[CLJS-1408] goog-define docstring wrong Created: 09/Aug/15  Updated: 09/Aug/15  Resolved: 09/Aug/15

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

Type: Defect Priority: Minor
Reporter: Martin Klepsch Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File 0001-fix-goog-define-docstring.patch    
Patch: Code

 Description   

add-implicit-options only munges symbol keys are untouched.
Thus the example given in the docstring is wrong.



 Comments   
Comment by David Nolen [ 09/Aug/15 10:29 AM ]

fixed https://github.com/clojure/clojurescript/commit/f0c7d4c34b1cebeec38f26a098651dc34fcfaecb





[CLJS-1409] Allow type checking of protocols Created: 09/Aug/15  Updated: 09/Aug/15  Resolved: 09/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Comments   
Comment by David Nolen [ 09/Aug/15 10:27 AM ]

fixed https://github.com/clojure/clojurescript/commit/27af25a3e43e4b61ba924f77ed5068717613e026





[CLJS-1405] type resolution needs to handle modifiers like leading !, trailing =, object syntax Created: 08/Aug/15  Updated: 08/Aug/15  Resolved: 08/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Comments   
Comment by David Nolen [ 08/Aug/15 3:12 PM ]

fixed https://github.com/clojure/clojurescript/commit/afc57a7df587faf28ba3116d8789d6c9e6b217de





[CLJS-1393] *target* is not overridden by :target :nodejs when :optimizations :none Created: 05/Aug/15  Updated: 08/Aug/15  Resolved: 08/Aug/15

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

Type: Defect Priority: Major
Reporter: Michael Ballantyne Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: newbie

Attachments: Text File 0001-CLJS-1393-turn-target-into-goog-define.patch    

 Description   

target at https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L20 is an @define variable, but doesn't use the goog-define macro. When configuring a build with :target :nodejs :optimizations :none, target still dereferences to the value "default".

Can this be made a goog-define?



 Comments   
Comment by Martin Klepsch [ 08/Aug/15 6:20 AM ]

Seems that simply changing the def to goog-define is enough

Comment by David Nolen [ 08/Aug/15 10:07 AM ]

fixed https://github.com/clojure/clojurescript/commit/8ff447cd9272cbfa4aa3cf5b2b42fa579c1d18ae





[CLJS-1401] Unify runtime & compile time UUID hash calculation Created: 08/Aug/15  Updated: 08/Aug/15  Resolved: 08/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Comments   
Comment by David Nolen [ 08/Aug/15 10:06 AM ]

fixed https://github.com/clojure/clojurescript/commit/e351c98538ccfd98871d071a4389e9e20dfba1b6





[CLJS-1399] Unnecessarily escaped forward-slash in regex produces invalid JS Created: 07/Aug/15  Updated: 07/Aug/15  Resolved: 07/Aug/15

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

Type: Defect Priority: Minor
Reporter: Chris Truter Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: None


 Description   

When compiling an escaped forward slash in a regex literal (muscle memory from Javascript syntax), ClojureScript will emit invalid Javascript, ie. it will emit an escaped back slash instead, and a terminating forward slash.

In bare node repl:

(re-find #"\/" "a/b")

generates the invalid expression:

(function (){var ret__3746__auto__ = cljs.core.re_find.call(null,/\\//,"a/b");

Interestingly both this and the unescaped variation (#"/") work correctly in Planck (and regular Clojure).

Creating the regex manually throws a reader error, because the string literal is illegal:

cljs.user=> "\/"
clojure.lang.ExceptionInfo: Unsupported escape character: \/ {:type :reader-exception, ...}

Unsure of whether a reader error or "we know what you meant" behaviour is preferable.



 Comments   
Comment by David Nolen [ 07/Aug/15 6:36 AM ]

Wary of "we know what you meant". I suspect there will be many cases like this. By the same token wary of specific error messages here, how many cases will we need to cover?

Comment by Chris Truter [ 07/Aug/15 8:00 AM ]

Thanks for the help David, I've confirmed this is an issue in tools.reader.





[CLJS-1398] (update-in {} [] assoc :a 1) yields {nil {:a 1}} Created: 07/Aug/15  Updated: 07/Aug/15  Resolved: 07/Aug/15

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

Type: Defect Priority: Major
Reporter: Mike Longworth Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: None


 Description   

Current behaviour:

(update-in {} [:x :y] assoc :a 1)
{:x {:y {:a 1}}}

(update-in {} [:x] assoc :a 1)
{:x {:a 1}}

(update-in {} [] assoc :a 1)
{nil {:a 1}}

I would expect: (update-in {} [] assoc :a 1)
to yield: {:a 1}



 Comments   
Comment by Francis Avila [ 07/Aug/15 4:01 AM ]

Clojure has the same behavior, and assoc-in also behaves similarly.

This is a long-standing bug (or wart, or at least personal pet-peeve) that is probably never going to be fixed for unknown reasons. Related tickets:

CLJ-373 CLJ-1520 CLJ-1064 CLJ-1623

Comment by Mike Longworth [ 07/Aug/15 4:29 AM ]

Ok thanks Francis, I should have dug deeper myself on this one; an easy local fix but it was the cause of some less than obvious issues.





[CLJS-1395] closure won't compile goog-define output Created: 06/Aug/15  Updated: 06/Aug/15  Resolved: 06/Aug/15

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

Type: Defect Priority: Major
Reporter: Michael Ballantyne Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File CLJS-1395-no-trailing-semicolons-after-JS-comments.patch    

 Description   

The goog-define macro translates a declaration like

(goog-define "myvar" 5)

to

/** @define {number} */;

goog.define("cljs_bootstrap.core.myvar",(5));}}

Compiling with :optimizations :simple leads to an error like this:

WARNING: /Users/michaelb/Desktop/nodebuild/goog-define-bug/test/out/goog_define_test/core.js:5: WARNING - Misplaced type annotation. Type annotations are not allowed here. Are you missing parentheses?
/** @define {number} */;

Closure doesn't seem to like the semicolon between the comment and the goog.define().



 Comments   
Comment by Michael Ballantyne [ 06/Aug/15 1:24 PM ]

I should've provided the complete error:

Reading analysis cache for jar:file:/Users/michaelb/.m2/repository/org/clojure/clojurescript/1.7.61/clojurescript-1.7.61.jar!/cljs/core.cljs
Compiling src/goog-define-test/core.cljs
Compiling out/cljs/core.cljs
Applying optimizations :simple to 13 sources
Aug 06, 2015 12:06:33 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/michaelb/Desktop/nodebuild/goog-define-bug/test/out/goog_define_test/core.js:5: WARNING - Misplaced type annotation. Type annotations are not allowed here. Are you missing parentheses?
/** @define {number} */;
                       ^

Aug 06, 2015 12:06:33 PM com.google.javascript.jscomp.LoggerErrorManager println
SEVERE: /Users/michaelb/Desktop/nodebuild/goog-define-bug/test/out/goog_define_test/core.js:7: ERROR - Missing @define annotation
goog.define("goog_define_test.core.myvar",(5));
^

Aug 06, 2015 12:06:33 PM com.google.javascript.jscomp.LoggerErrorManager printSummary
WARNING: 1 error(s), 1 warning(s)
ERROR: JSC_INVALID_MISSING_DEFINE_ANNOTATION. Missing @define annotation at /Users/michaelb/Desktop/nodebuild/goog-define-bug/test/out/goog_define_test/core.js line 7 : 0
WARNING: JSC_MISPLACED_ANNOTATION. Misplaced type annotation. Type annotations are not allowed here. Are you missing parentheses? at /Users/michaelb/Desktop/nodebuild/goog-define-bug/test/out/goog_define_test/core.js line 5 : 23
Comment by Martin Klepsch [ 06/Aug/15 2:36 PM ]

Attached a patch with rudimentary checking if a comment is being emitted.
If a comment is emitted it's not emitted via emit-wrap but plain emits.

Comment by David Nolen [ 06/Aug/15 6:17 PM ]

fixed https://github.com/clojure/clojurescript/commit/c99f65f28aa46f28167815bcef2244ef473c4844





[CLJS-1396] Allow starting Node.js with debug port set Created: 06/Aug/15  Updated: 06/Aug/15  Resolved: 06/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Description   

Currently this is not configurable. Should be able to supply :debug-port.



 Comments   
Comment by David Nolen [ 06/Aug/15 2:38 PM ]

fixed https://github.com/clojure/clojurescript/commit/139da195125110646f8d5550fc753fdbf63f565c





[CLJS-1394] reify gensyms can clash Created: 05/Aug/15  Updated: 05/Aug/15  Resolved: 05/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: None


 Description   

The gensym is naive, should be namespaced.



 Comments   
Comment by David Nolen [ 05/Aug/15 7:27 PM ]

fixed https://github.com/clojure/clojurescript/commit/e49404647f75f2790e73268625cfd643d12d199b





[CLJS-994] print a warning when :externs file paths can't be found. Created: 30/Jan/15  Updated: 05/Aug/15

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

Type: Enhancement Priority: Major
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-1302] (js/goog.base (js* "this")) no longer works Created: 09/Jun/15  Updated: 05/Aug/15

Status: In Progress
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
Fix Version/s: Next

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


 Description   
(defn Foo
  []
  (goog/base (js* "this")))
(goog/inherits Foo js/Object)

(defn Foo
  []
  (this-as this
    (goog/base this)))
(goog/inherits Foo js/Object)


 Comments   
Comment by David Nolen [ 01/Aug/15 6:12 AM ]

90% sure this is because of the SSA bit for fn invokes under advanced optimizations.





[CLJS-1344] port Clojure tuples commit Created: 17/Jul/15  Updated: 05/Aug/15

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

Type: Task Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File 0001-CLJS-1344-port-Clojure-tuples-commit-of-16-July-2015.patch     Text File 0002-CLJS-1344-port-Clojure-tuples-commit-of-16-July-2015.patch     Text File 0003-CLJS-1344-port-Clojure-tuples-commit-of-16-July-2015.patch    

 Description   

See https://github.com/clojure/clojure/commit/36d665793b43f62cfd22354aced4c6892088abd6



 Comments   
Comment by Michał Marczyk [ 18/Jul/15 11:38 AM ]

Patch based on current master.

Comment by Michał Marczyk [ 18/Jul/15 11:50 AM ]

In absence of abstract bases macros seemed like the most straightforward way to keep things DRY. Anything involving transients or metadata still uses PV, as in Clojure.

Comment by Michał Marczyk [ 18/Jul/15 12:04 PM ]

The 0002 patch is the same, except it does NOT change PV's -equiv to check satisfies? IVector rather than instance? PersistentVector. (The 0001 patch does make this change.)

Haven't made up my mind as to whether it's better to switch or not, so I thought I'd prepare both versions.

Comment by Michał Marczyk [ 18/Jul/15 12:25 PM ]

Some benchmark results, for now obtained using SpiderMonkey (I've just realized that I don't have a working V8 setup on this box – which is not the one I normally use for CLJS dev – I'll have to look into fixing that).

On the subject of -equiv, script benchmark says 0002 patch is very slightly faster than 0001 patch, which itself is noticeably faster than master: 633 ms vs 660 ms vs 781 ms in the vector equality benchmark.

The reason both patches are faster than master is undoubtedly their choice to call -count rather than count on the "other thing"; that is 100% justified in the 0002 patch (with instance?) and slightly less justified in the 0001 patch (who could implement IVector without ICounted though?).

Full script/benchmark results:

master
======

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 585 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 137 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 735 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 3 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 297 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 23 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 21 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 32 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 524 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1879 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 789 msecs
[coll "foobar"], (first coll), 1000000 runs, 1762 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 150 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 1297 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 587 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 1486 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 105 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 214 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 114 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 111 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 88 msecs
[], (list), 1000000 runs, 13 msecs
[], (list 1 2 3), 1000000 runs, 1691 msecs

;;; vector ops
[], [], 1000000 runs, 9 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 715 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0xd0fabc8 "cljs.tagged_literals.JSValue@d0fabc8"])), 1000000 runs, 972 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 638 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 161 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 323 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 361 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 238 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 211 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 1284 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 1071 msecs
[coll []], (-conj coll 1), 1000000 runs, 1067 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 1133 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 835 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 472 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 557 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 91 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 104 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 690 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 435 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 425 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 251 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 160 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 213 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 184 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 207 msecs
[], (-next v), 1000000 runs, 768 msecs
[], (-rest v), 1000000 runs, 326 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 679 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 781 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 672 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 965 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 403 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 20 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 267 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 675 msecs
[], (list 1 2 3 4 5), 1000000 runs, 639 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 2422 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1725 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 1620 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 3240 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 2525 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 2980 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 453 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 549 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 265 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1695 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 276 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 252 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 2831 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 373 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 276 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 515 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 320 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 355 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 322 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 297 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 416 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 360 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 331 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 894 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 732 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 1027 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 699 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 589 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 330 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 523 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 307 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 530 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 295 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 574 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 291 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 280 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 9 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 251 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 253 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 322 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 336 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 373 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 626 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 266 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 244 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 267 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 340 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 128 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 111 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 361 msecs
[coll pmap], (:f0 coll), 1000000 runs, 385 msecs
[coll pmap], (get coll :f0), 1000000 runs, 370 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 324 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 364 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 3598 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 657 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 565 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 421 msecs

transient map, conj! 100000 items
"Elapsed time: 539 msecs"


;;; set ops
[], #{}, 1000000 runs, 217 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 587 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 477 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 304 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 267 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 289 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 21 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 85 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 1060 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 77 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 369 msecs
;;; second run
[r r], (last r), 1 runs, 94 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 233 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 473 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 1233 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 688 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 53 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 52 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 177 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1298 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 57 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 569 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 67 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 82 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 134 msecs

0001 patch
==========

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 585 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 137 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 735 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 3 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 297 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 23 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 21 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 32 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 524 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1879 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 789 msecs
[coll "foobar"], (first coll), 1000000 runs, 1762 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 150 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 1297 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 587 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 1486 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 105 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 214 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 114 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 111 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 88 msecs
[], (list), 1000000 runs, 13 msecs
[], (list 1 2 3), 1000000 runs, 1691 msecs

;;; vector ops
[], [], 1000000 runs, 9 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 715 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0xd0fabc8 "cljs.tagged_literals.JSValue@d0fabc8"])), 1000000 runs, 972 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 638 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 161 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 323 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 361 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 238 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 211 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 1284 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 1071 msecs
[coll []], (-conj coll 1), 1000000 runs, 1067 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 1133 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 835 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 472 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 557 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 91 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 104 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 690 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 435 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 425 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 251 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 160 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 213 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 184 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 207 msecs
[], (-next v), 1000000 runs, 768 msecs
[], (-rest v), 1000000 runs, 326 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 679 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 781 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 672 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 965 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 403 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 20 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 267 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 675 msecs
[], (list 1 2 3 4 5), 1000000 runs, 639 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 2422 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1725 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 1620 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 3240 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 2525 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 2980 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 453 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 549 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 265 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1695 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 276 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 252 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 2831 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 373 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 276 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 515 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 320 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 355 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 322 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 297 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 416 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 360 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 331 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 894 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 732 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 1027 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 699 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 589 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 330 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 523 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 307 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 530 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 295 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 574 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 291 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 280 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 9 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 251 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 253 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 322 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 336 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 373 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 626 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 266 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 244 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 267 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 340 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 128 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 111 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 361 msecs
[coll pmap], (:f0 coll), 1000000 runs, 385 msecs
[coll pmap], (get coll :f0), 1000000 runs, 370 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 324 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 364 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 3598 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 657 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 565 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 421 msecs

transient map, conj! 100000 items
"Elapsed time: 539 msecs"


;;; set ops
[], #{}, 1000000 runs, 217 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 587 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 477 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 304 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 267 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 289 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 21 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 85 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 1060 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 77 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 369 msecs
;;; second run
[r r], (last r), 1 runs, 94 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 233 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 473 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 1233 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 688 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 53 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 52 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 177 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1298 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 57 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 569 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 67 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 82 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 134 msecs

0002 patch
==========

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 645 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 95 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 557 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 2 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 460 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 18 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 32 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 36 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 613 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1658 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 834 msecs
[coll "foobar"], (first coll), 1000000 runs, 1934 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 219 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 1371 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 444 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 351 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 125 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 138 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 97 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 109 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 80 msecs
[], (list), 1000000 runs, 10 msecs
[], (list 1 2 3), 1000000 runs, 1387 msecs

;;; vector ops
[], [], 1000000 runs, 10 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 316 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x57b33c29 "cljs.tagged_literals.JSValue@57b33c29"])), 1000000 runs, 732 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 281 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 484 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 112 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 162 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 194 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 125 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 756 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 606 msecs
[coll []], (-conj coll 1), 1000000 runs, 515 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 648 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 422 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 566 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 89 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 154 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 89 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 631 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 450 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 547 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 589 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 204 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 177 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 143 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 141 msecs
[], (-next v), 1000000 runs, 529 msecs
[], (-rest v), 1000000 runs, 236 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 924 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 633 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 610 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 1138 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 545 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 121 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 281 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 597 msecs
[], (list 1 2 3 4 5), 1000000 runs, 560 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 2573 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1927 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 6163 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 3149 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 1883 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 659 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 611 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 556 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 368 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1707 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 280 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 266 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 2862 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 356 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 391 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 439 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 363 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 321 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 405 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 328 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 444 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 330 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 353 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 1427 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 589 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 1087 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 674 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 719 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 287 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 841 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 327 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 625 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 294 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 630 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 314 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 312 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 10 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 269 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 268 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 309 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 440 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 404 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 756 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 290 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 326 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 279 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 338 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 138 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 177 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 385 msecs
[coll pmap], (:f0 coll), 1000000 runs, 411 msecs
[coll pmap], (get coll :f0), 1000000 runs, 439 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 336 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 457 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 4330 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 831 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 490 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 474 msecs

transient map, conj! 100000 items
"Elapsed time: 565 msecs"


;;; set ops
[], #{}, 1000000 runs, 225 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 711 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 608 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 353 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 322 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 335 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 22 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 99 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 1538 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 37 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 348 msecs
;;; second run
[r r], (last r), 1 runs, 71 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 433 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 287 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 1191 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 831 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 52 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 81 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 207 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1375 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 73 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 429 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 51 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 73 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 133 msecs
Comment by David Nolen [ 19/Jul/15 11:06 AM ]

Thanks will review.

Comment by David Nolen [ 20/Jul/15 2:33 PM ]

This ticket should probably be updated with the latest equiv changes in Clojure master no?

Comment by Michał Marczyk [ 20/Jul/15 2:49 PM ]

Right, will do (plus a rebase on top of current master while I'm at it).

Comment by Michał Marczyk [ 20/Jul/15 6:43 PM ]

New 0003 patch superseding the previous ones attached.

See some new benchmark results below. There are some apparent substantial speedups where I would expect them, there's the somewhat expected slowdown for transient with small vectors.

Freak result: most large vector ops stay around their original spots as expected, except for reduce conj [] over a long range, which becomes weirdly slow. This I find hard to explain, particularly since ranges are used in other benchmarks as well, and those behave sensibly.

I tried compiling the benchmark suite with :optimizations :simple to see if the freak result was something obvious maybe. Oddly enough, all/most timings are significantly better under :simple. Am I missing something obvious here…?

1. master:

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 506 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 70 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 554 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 3 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 313 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 15 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 19 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 33 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 401 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1202 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 563 msecs
[coll "foobar"], (first coll), 1000000 runs, 1307 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 161 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 949 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 379 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 1025 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 59 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 88 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 67 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 80 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 62 msecs
[], (list), 1000000 runs, 10 msecs
[], (list 1 2 3), 1000000 runs, 1132 msecs

;;; vector ops
[], [], 1000000 runs, 10 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 495 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x56b66a26 "cljs.tagged_literals.JSValue@56b66a26"])), 1000000 runs, 547 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 435 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 120 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 197 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 95 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 199 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 209 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 893 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 842 msecs
[coll []], (-conj coll 1), 1000000 runs, 765 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 854 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 631 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 413 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 668 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 163 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 89 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 497 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 319 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 316 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 172 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 240 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 128 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 80 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 105 msecs
[], (-next v), 1000000 runs, 460 msecs
[], (-rest v), 1000000 runs, 166 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 746 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 557 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 437 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 820 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 308 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 20 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 250 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 471 msecs
[], (list 1 2 3 4 5), 1000000 runs, 410 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 1898 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1506 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 954 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 2495 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 1864 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 2799 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 2367 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 465 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 268 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1228 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 251 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 270 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 3502 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 330 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 294 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 528 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 282 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 333 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 318 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 286 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 409 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 341 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 353 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 878 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 589 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 972 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 582 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 850 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 269 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 793 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 269 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 596 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 311 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 586 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 321 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 280 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 10 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 254 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 250 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 288 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 322 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 305 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 537 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 257 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 250 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 238 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 338 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 123 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 114 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 342 msecs
[coll pmap], (:f0 coll), 1000000 runs, 368 msecs
[coll pmap], (get coll :f0), 1000000 runs, 356 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 274 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 290 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 3028 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 641 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 412 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 411 msecs

transient map, conj! 100000 items
"Elapsed time: 505 msecs"


;;; set ops
[], #{}, 1000000 runs, 215 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 460 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 516 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 293 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 269 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 290 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 18 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 77 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 957 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 42 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 310 msecs
;;; second run
[r r], (last r), 1 runs, 71 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 234 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 416 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 981 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 699 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 50 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 52 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 184 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1278 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 46 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 333 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 216 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 71 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 142 msecs

2. 0003 patch:

Benchmarking with SpiderMonkey
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 480 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 71 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 476 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 3 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 621 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 28 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 30 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 54 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 542 msecs
[coll "foobar"], (seq coll), 1000000 runs, 1209 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 564 msecs
[coll "foobar"], (first coll), 1000000 runs, 1257 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 140 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 913 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 424 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 170 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 58 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 89 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 69 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 80 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 61 msecs
[], (list), 1000000 runs, 10 msecs
[], (list 1 2 3), 1000000 runs, 1142 msecs

;;; vector ops
[], [], 1000000 runs, 10 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 272 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x3ff26c9 "cljs.tagged_literals.JSValue@3ff26c9"])), 1000000 runs, 585 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 240 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 273 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 101 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 102 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 102 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 38 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 429 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 392 msecs
[coll []], (-conj coll 1), 1000000 runs, 368 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 395 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 364 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 383 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 75 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 142 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 79 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 395 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 408 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 383 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 406 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 269 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 131 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 80 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 104 msecs
[], (-next v), 1000000 runs, 461 msecs
[], (-rest v), 1000000 runs, 171 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 592 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 562 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 467 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 830 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 426 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 15 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 265 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 580 msecs
[], (list 1 2 3 4 5), 1000000 runs, 386 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 1885 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 1362 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 4564 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 2536 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 1940 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 1948 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 452 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 484 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 264 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 1540 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 294 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 251 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 3150 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 378 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 278 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 507 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 288 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 339 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 324 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 301 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 533 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 355 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 309 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 757 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 514 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 844 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 622 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 765 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 271 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 521 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 276 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 515 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 314 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 534 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 324 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 327 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 10 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 290 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 254 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 301 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 347 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 365 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 496 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 242 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 254 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 262 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 319 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 128 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 124 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 368 msecs
[coll pmap], (:f0 coll), 1000000 runs, 446 msecs
[coll pmap], (get coll :f0), 1000000 runs, 511 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 328 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 319 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 4954 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 963 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 425 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 421 msecs

transient map, conj! 100000 items
"Elapsed time: 531 msecs"


;;; set ops
[], #{}, 1000000 runs, 230 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 679 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 605 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 295 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 273 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 321 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 22 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 73 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 934 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 33 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 310 msecs
;;; second run
[r r], (last r), 1 runs, 60 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 230 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 400 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 865 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 627 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 49 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 55 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 197 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1296 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 48 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 502 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 50 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 434 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 132 msecs
Comment by David Nolen [ 20/Jul/15 10:35 PM ]

Michal it might be a GC thing? Not sure. These tests need to be run on more engines, kinda wish we had something a bit more visual by now

Comment by Michał Marczyk [ 20/Jul/15 11:32 PM ]

Indeed… I got the V8 situation sorted in the meantime and benchmarked master vs 0003 with a fresh build; I thought the results were pretty encouraging, particularly the 2x speedup for "small vector conj".

1. master:

Benchmarking with V8
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 63 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 24 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 21 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 14 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 14 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 6 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 12 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 21 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 26 msecs
[coll "foobar"], (seq coll), 1000000 runs, 31 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 39 msecs
[coll "foobar"], (first coll), 1000000 runs, 59 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 37 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 139 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 26 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 61 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 20 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 16 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 13 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 10 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 11 msecs
[], (list), 1000000 runs, 5 msecs
[], (list 1 2 3), 1000000 runs, 67 msecs

;;; vector ops
[], [], 1000000 runs, 4 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 71 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x158e6fc2 "cljs.tagged_literals.JSValue@158e6fc2"])), 1000000 runs, 56 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 83 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 28 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 23 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 25 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 22 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 27 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 64 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 60 msecs
[coll []], (-conj coll 1), 1000000 runs, 53 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 54 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 53 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 26 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 25 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 19 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 16 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 24 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 22 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 26 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 56 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 27 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 64 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 23 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 14 msecs
[], (-next v), 1000000 runs, 32 msecs
[], (-rest v), 1000000 runs, 33 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 41 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 36 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 327 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 974 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 74 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 16 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 28 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 61 msecs
[], (list 1 2 3 4 5), 1000000 runs, 146 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 121 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 809 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 141 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 201 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 483 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 290 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 295 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 271 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 27 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 711 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 23 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 24 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 1284 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 66 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 43 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 72 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 52 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 56 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 79 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 57 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 87 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 61 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 70 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 142 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 91 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 171 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 99 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 106 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 34 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 84 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 32 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 82 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 40 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 119 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 47 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 70 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 9 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 36 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 29 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 60 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 69 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 47 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 257 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 13 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 24 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 13 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 80 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 75 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 91 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 83 msecs
[coll pmap], (:f0 coll), 1000000 runs, 64 msecs
[coll pmap], (get coll :f0), 1000000 runs, 56 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 45 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 78 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 224 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 405 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 83 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 155 msecs

transient map, conj! 100000 items
"Elapsed time: 48 msecs"


;;; set ops
[], #{}, 1000000 runs, 5 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 355 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 243 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 48 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 38 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 54 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 24 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 23 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 654 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 42 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 319 msecs
;;; second run
[r r], (last r), 1 runs, 87 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 246 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 163 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 134 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 22 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 85 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 105 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 359 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1413 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 80 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 134 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 8 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 30 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 95 msecs

2. 0003:

Benchmarking with V8
[x 1], (identity x), 1000000 runs, 0 msecs
;; symbol construction
[], (symbol (quote foo)), 1000000 runs, 51 msecs

;; array-reduce & ci-reduce
[coll (seq arr)], (ci-reduce coll + 0), 1 runs, 23 msecs
[coll (seq arr)], (ci-reduce coll sum 0), 1 runs, 19 msecs
[coll arr], (array-reduce coll + 0), 1 runs, 13 msecs
[coll arr], (array-reduce coll sum 0), 1 runs, 11 msecs
;;; instance?
[coll []], (instance? PersistentVector coll), 1000000 runs, 6 msecs
;;; satisfies?
[coll (list 1 2 3)], (satisfies? ISeq coll), 1000000 runs, 12 msecs
[coll [1 2 3]], (satisfies? ISeq coll), 1000000 runs, 22 msecs

;;; array & string ops
[coll (array 1 2 3)], (seq coll), 1000000 runs, 27 msecs
[coll "foobar"], (seq coll), 1000000 runs, 39 msecs
[coll (array 1 2 3)], (first coll), 1000000 runs, 34 msecs
[coll "foobar"], (first coll), 1000000 runs, 44 msecs
[coll (array 1 2 3)], (nth coll 2), 1000000 runs, 35 msecs
[coll "foobar"], (nth coll 2), 1000000 runs, 137 msecs

;;; cloning & specify
[coll [1 2 3]], (clone coll), 1000000 runs, 23 msecs
[coll [1 2 3]], (specify coll IFoo (foo [_] :bar)), 1000000 runs, 69 msecs
[coll (specify [1 2 3] IFoo (foo [_] :bar))], (foo coll), 1000000 runs, 20 msecs

;;; list ops
[coll (list 1 2 3)], (first coll), 1000000 runs, 16 msecs
[coll (list 1 2 3)], (-first coll), 1000000 runs, 13 msecs
[coll (list 1 2 3)], (rest coll), 1000000 runs, 10 msecs
[coll (list 1 2 3)], (-rest coll), 1000000 runs, 10 msecs
[], (list), 1000000 runs, 4 msecs
[], (list 1 2 3), 1000000 runs, 67 msecs

;;; vector ops
[], [], 1000000 runs, 4 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count [a b c]), 1000000 runs, 40 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vec #object[cljs.tagged_literals.JSValue 0x3e5ebdfe "cljs.tagged_literals.JSValue@3e5ebdfe"])), 1000000 runs, 55 msecs
[[a b c] (take 3 (repeatedly (fn* [] (rand-int 10))))], (-count (vector a b c)), 1000000 runs, 46 msecs
[coll [1 2 3]], (transient coll), 100000 runs, 31 msecs
[coll [1 2 3]], (nth coll 0), 1000000 runs, 16 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 16 msecs
[coll [1 2 3]], (-nth coll 0), 1000000 runs, 16 msecs
[coll [1 2 3]], (coll 0), 1000000 runs, 24 msecs
[coll [1 2 3]], (conj coll 4), 1000000 runs, 31 msecs
[coll [1 2 3]], (-conj coll 4), 1000000 runs, 29 msecs
[coll []], (-conj coll 1), 1000000 runs, 25 msecs
[coll [1]], (-conj coll 2), 1000000 runs, 22 msecs
[coll [1 2]], (-conj coll 3), 1000000 runs, 26 msecs
[coll [1 2 3]], (seq coll), 1000000 runs, 26 msecs
[coll [1 2 3]], (-seq coll), 1000000 runs, 26 msecs
[coll (seq [1 2 3])], (first coll), 1000000 runs, 28 msecs
[coll (seq [1 2 3])], (-first coll), 1000000 runs, 24 msecs
[coll (seq [1 2 3])], (rest coll), 1000000 runs, 30 msecs
[coll (seq [1 2 3])], (-rest coll), 1000000 runs, 26 msecs
[coll (seq [1 2 3])], (next coll), 1000000 runs, 29 msecs

;;; large vector ops
[], (reduce conj [] (range 40000)), 10 runs, 60 msecs
[coll (reduce conj [] (range (+ 32768 32)))], (conj coll :foo), 100000 runs, 32 msecs
[coll (reduce conj [] (range 40000))], (assoc coll 123 :foo), 100000 runs, 63 msecs
[coll (reduce conj [] (range (+ 32768 33)))], (pop coll), 100000 runs, 22 msecs

;;; chunked seqs
[], (-first v), 1000000 runs, 14 msecs
[], (-next v), 1000000 runs, 36 msecs
[], (-rest v), 1000000 runs, 33 msecs

;;; transients
transient vector, conj! 1000000 items
"Elapsed time: 44 msecs"


;;; vector equality
[a (into [] (range 1000000)) b (into [] (range 1000000))], (= a b), 1 runs, 37 msecs

;;; keyword compare
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed)))))], (.sort arr compare), 100 runs, 281 msecs
[arr (into-array (repeatedly 10000 (fn* [] (keyword (rand-nth seed) (rand-nth seed)))))], (.sort arr compare), 100 runs, 1025 msecs

;;; reduce lazy-seqs, vectors, ranges
[coll (take 100000 (iterate inc 0))], (reduce + 0 coll), 1 runs, 75 msecs
[coll (range 1000000)], (reduce + 0 coll), 1 runs, 17 msecs
[coll (into [] (range 1000000))], (reduce + 0 coll), 1 runs, 25 msecs

;; apply
[coll (into [] (range 1000000))], (apply + coll), 1 runs, 69 msecs
[], (list 1 2 3 4 5), 1000000 runs, 206 msecs
[xs (array-seq (array 1 2 3 4 5))], (apply list xs), 1000000 runs, 121 msecs
[xs (list 1 2 3 4 5)], (apply list xs), 1000000 runs, 757 msecs
[xs [1 2 3 4 5]], (apply list xs), 1000000 runs, 924 msecs
[f (fn [a b & more])], (apply f (range 32)), 1000000 runs, 184 msecs
[f (fn [a b c d e f g h i j & more])], (apply f (range 32)), 1000000 runs, 453 msecs

;; update-in
[coll {:foo 1} ks [:foo]], (update-in coll ks inc), 1000000 runs, 320 msecs
[coll (array-map :foo 1) ks [:foo]], (update-in coll ks inc), 1000000 runs, 301 msecs

;;; obj-map
[coll (obj-map)], (assoc coll :foo :bar), 1000000 runs, 267 msecs
[coll (obj-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 27 msecs
[coll (obj-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 727 msecs
[coll (obj-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 24 msecs
[coll (obj-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 25 msecs

;;; array-map
[], {[1] true, [2] true, [3] true}, 1000000 runs, 762 msecs
[coll (array-map)], (assoc coll :foo :bar), 1000000 runs, 66 msecs
[coll (array-map :foo :bar)], (-lookup coll :foo), 1000000 runs, 50 msecs
[coll (array-map :foo :bar)], (assoc coll :baz :woz), 1000000 runs, 74 msecs
[coll (array-map :foo :bar :baz :woz)], (-lookup coll :baz), 1000000 runs, 56 msecs
[coll (array-map :foo :bar :baz :woz :lol :rofl)], (-lookup coll :lol), 1000000 runs, 62 msecs

;;; array-map w/ symbols
[coll (array-map)], (assoc coll a b), 1000000 runs, 82 msecs
[coll (array-map a b)], (-lookup coll a), 1000000 runs, 61 msecs
[coll (array-map a b)], (assoc coll c d), 1000000 runs, 90 msecs
[coll (array-map a b c d)], (-lookup coll c), 1000000 runs, 65 msecs
[coll (array-map a b c d e f)], (-lookup coll e), 1000000 runs, 72 msecs

;;; array-map w/ inline symbols
[coll (array-map)], (assoc coll (quote foo) (quote bar)), 1000000 runs, 142 msecs
[coll (array-map (quote foo) (quote bar))], (-lookup coll (quote foo)), 1000000 runs, 92 msecs
[coll (array-map (quote foo) (quote bar))], (assoc coll (quote baz) (quote woz)), 1000000 runs, 163 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz))], (-lookup coll (quote baz)), 1000000 runs, 102 msecs
[coll (array-map (quote foo) (quote bar) (quote baz) (quote woz) (quote lol) (quote rofl))], (-lookup coll (quote lol)), 1000000 runs, 105 msecs

;;; map / record ops
[coll {:foo 1, :bar 2}], (get coll :foo), 1000000 runs, 35 msecs
[coll {(quote foo) 1, (quote bar) 2}], (get coll (quote foo)), 1000000 runs, 86 msecs
[coll {:foo 1, :bar 2}], (-lookup coll :foo nil), 1000000 runs, 31 msecs
[coll {(quote foo) 1, (quote bar) 2}], (-lookup coll (quote foo) nil), 1000000 runs, 80 msecs
[coll {:foo 1, :bar 2}], (:foo coll), 1000000 runs, 42 msecs
[coll {(quote foo) 1, (quote bar) 2}], ((quote foo) coll), 1000000 runs, 116 msecs
[coll {:foo 1, :bar 2}], (kw coll), 1000000 runs, 47 msecs
[coll {(quote foo) 1, (quote bar) 2}], (sym coll), 1000000 runs, 67 msecs
[coll {:foo 1, :bar 2}], (loop [i 0 m coll] (if (< i 100000) (recur (inc i) (assoc m :foo 2)) m)), 1 runs, 5 msecs
[coll (Foo. 1 2)], (:bar coll), 1000000 runs, 35 msecs
[coll (Foo. 1 2)], (-lookup coll :bar), 1000000 runs, 30 msecs
[coll (Foo. 1 2)], (assoc coll :bar 2), 1000000 runs, 52 msecs
[coll (Foo. 1 2)], (assoc coll :baz 3), 1000000 runs, 61 msecs
[coll (Foo. 1 2)], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :bar 2)) m)), 1 runs, 44 msecs

;;; zipmap
[m {:a 1, :b 2, :c 3}], (zipmap (keys m) (map inc (vals m))), 100000 runs, 230 msecs

;;; persistent hash maps
[key :f0], (hash key), 1000000 runs, 13 msecs
[key "f0"], (m3-hash-unencoded-chars key), 1000000 runs, 22 msecs
[key :unsynchronized-mutable], (hash key), 1000000 runs, 11 msecs
[coll hash-coll-test], (hash-coll coll), 100 runs, 70 msecs
[coll hash-coll-test], (hash-ordered-coll coll), 100 runs, 65 msecs
[coll hash-imap-test], (hash-imap coll), 100 runs, 73 msecs
[coll hash-imap-test], (hash-unordered-coll coll), 100 runs, 96 msecs
[coll pmap], (:f0 coll), 1000000 runs, 64 msecs
[coll pmap], (get coll :f0), 1000000 runs, 52 msecs
[coll pmap], (-lookup coll :f0 nil), 1000000 runs, 47 msecs
[coll pmap], (-lookup hash-imap-test :foo500 nil), 1000000 runs, 79 msecs
[coll pmap], (-lookup hash-imap-int-test 500 nil), 1000000 runs, 216 msecs
[coll pmap], (assoc coll :g0 32), 1000000 runs, 405 msecs
[coll pmap], (loop [i 0 m coll] (if (< i 1000000) (recur (inc i) (assoc m :a 1)) m)), 1 runs, 80 msecs
[coll cljs.core.PersistentHashMap.EMPTY], (assoc coll :f0 1), 1000000 runs, 156 msecs

transient map, conj! 100000 items
"Elapsed time: 53 msecs"


;;; set ops
[], #{}, 1000000 runs, 5 msecs
[], #{1 3 2}, 1000000 runs, 0 msecs
[v [1 2 3]], (set v), 1000000 runs, 515 msecs
[], (hash-set 1 2 3), 1000000 runs, 0 msecs
[coll #{1 3 2}], (conj coll 4), 1000000 runs, 266 msecs
[coll #{1 3 2}], (get coll 2), 1000000 runs, 50 msecs
[coll #{1 3 2}], (contains? coll 2), 1000000 runs, 38 msecs
[coll #{1 3 2}], (coll 2), 1000000 runs, 62 msecs

;;; seq ops
[coll (range 500000)], (reduce + coll), 1 runs, 23 msecs

;;; reader
[s "{:foo [1 2 3]}"], (reader/read-string s), 1000 runs, 30 msecs
[s big-str-data], (reader/read-string s), 1000 runs, 636 msecs

;;; range
[r (range 1000000)], (last r), 1 runs, 44 msecs

;;; lazy-seq
;;; first run
[r r], (last r), 1 runs, 322 msecs
;;; second run
[r r], (last r), 1 runs, 90 msecs

;;; comprehensions
[xs (range 512)], (last (for [x xs y xs] (+ x y))), 1 runs, 229 msecs
[xs (vec (range 512))], (last (for [x xs y xs] (+ x y))), 4 runs, 196 msecs
[a (Box. 0) xs (range 512)], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 133 msecs
[a (Box. 0) xs (vec (range 512))], (doseq [x xs y xs] (set! a -val (+ (.-val a) x))), 4 runs, 23 msecs

;; reducers
[xs (into [] (range 1000000))], (r/reduce + (r/map inc (r/map inc (r/map inc xs)))), 1 runs, 85 msecs
;; transducers
[xs (into [] (range 1000000))], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 108 msecs
;; primitive array reduce 1000000 many ops
[xs (into-array (range 1000000))], (-> xs (.map inc) (.map inc) (.map inc) (.reduce (fn [a b] (+ a b)) 0)), 1 runs, 349 msecs
;; reduce range 1000000 many ops
[xs (range 1000000)], (reduce + 0 (map inc (map inc (map inc xs)))), 1 runs, 1387 msecs
;; transduce range 1000000 many ops 
[xs (range 1000000)], (transduce (comp (map inc) (map inc) (map inc)) + 0 xs), 1 runs, 79 msecs


;; multimethods
[], (simple-multi :foo), 1000000 runs, 122 msecs


;; higher-order variadic function calls
[f array], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 8 msecs
[f vector], (f 1 2 3 4 5 6 7 8 9 0), 100000 runs, 154 msecs
[], (= 1 1 1 1 1 1 1 1 1 0), 100000 runs, 94 msecs
Comment by Michał Marczyk [ 20/Jul/15 11:35 PM ]

(No rebase this time, as 0003 still applies cleanly and all tests pass.)





[CLJS-712] resolve-var for symbol with dot still wrong Created: 03/Dec/13  Updated: 05/Aug/15

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

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


 Description   

We need to recur on the first segment passing an new additional argument to resolve-var indicating that we should not try to resolve in the current namespace and instead warn.






[CLJS-773] Use unchecked-*-int functions for real 32-bit math Created: 26/Feb/14  Updated: 05/Aug/15

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

Type: Enhancement Priority: Major
Reporter: Francis Avila Assignee: Francis Avila
Resolution: Unresolved Votes: 0
Labels: numerics
Environment:

r2173



 Description   

Currently the unchecked-* functions and macros simply alias the primitive js operators. It would be nice if the unchecked-*-int family of functions and macros implemented C/Java-like signed int operations with silent overflows (just like in Clojure) using asm.js coersion idioms. This should also allow us to share such code between clojure and clojurescript without worrying about their different numerics.

A use case is that porting hash algorithms from java to clojurescript is trickier and more verbose than it needs to be.



 Comments   
Comment by David Nolen [ 08/May/14 6:43 PM ]

This sounds interesting, would like to see more thoughts on approach, benchmarks etc.

Comment by David Nolen [ 02/Dec/14 5:46 AM ]

Bump, this enhancements sound simple & fine.

Comment by Francis Avila [ 02/Dec/14 1:26 PM ]

I'll have time to do this in about a week. The implementation is straightforward (basically use xor 0 everywhere). The goal is correctness, but I expect performance to be as good as or better than it is now on most platforms. I'm not sure if advanced mode will drop intermediate truncations or what impact this has on performance.

Some higher-level numeric analysis using the asm.js type system is possible but I doubt it's worth it.

Comment by Francis Avila [ 16/Mar/15 11:14 AM ]

I completely forgot about this, sorry. I see you have scheduled it for the "next" release. Are you assigning it as well or will you still accept a patch?

Comment by David Nolen [ 16/Mar/15 11:26 AM ]

Be my guest





[CLJS-818] Externs don't get loaded when running under immutant as cljs.js-deps/find-js-classpath fails Created: 18/Jun/14  Updated: 05/Aug/15

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

Type: Defect Priority: Major
Reporter: James Cash Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Java 1.8.0_05, Clojure 1.6.0, clojurescript 0.0-2234, immutant 1.1.1


Attachments: Text File 818.patch     PNG File Screen Shot 2014-06-18 at 20.14.59 .PNG    

 Description   

When compiling clojurescript that relies on library-provided externs (e.g. Om needing React.js externs), the clojurescript is compiled without errors, but the generated javascript fails to work, due to the externs not being loaded. Externs don't get loaded, as cljs.js-deps/find-js-classpath doesn't find the javascript externs file. This occurs because it uses cljs.js-deps/all-classpath-urls, which filters out the immutant classloader, since org.immutant.core.ImmutantClassLoader is not an instance of java.net.URLClassLoader (and hence lacks a .getURLs method anyway).



 Comments   
Comment by Toby Crawley [ 19/Jun/14 9:23 AM ]

Chas: Is there a reason not to depend on dynapath here? This exact case is kinda why it exists

Comment by David Nolen [ 19/Jun/14 10:47 AM ]

Patch welcome for this.

Comment by James Cash [ 19/Jun/14 2:12 PM ]

Simply replacing cljs.js-deps/all-classpath-urls with dynapath.util/all-classpath-urls worked for me. I don't know if there are policies around adding dependencies to cljs, but the following patch is working for me. Would it be preferable to re-implement the functionality instead?

Comment by David Nolen [ 19/Jun/14 2:19 PM ]

We are not going to take on a dependency for this. The code should be copied over, thanks.

Comment by James Cash [ 19/Jun/14 3:46 PM ]

Due to the way dynapath works, I don't think a straightforward copying of the code will work, since it relies on a protocol. Backing up a step though, would it be reasonable for externs to be loaded via io/resource, in the same way that the :preamble is?

Comment by Toby Crawley [ 19/Jun/14 3:54 PM ]

Unfortunately, the code can't be copied over. Dynapath works by providing a protocol that providers/users of funky classloaders can implement, allowing libraries that use dynapath to access the dynamic features of those classloaders without having to care about the loader's concrete type. Dynapath itself provides implementations for j.n.URLClassLoader and c.l.DynamicClassloader by default, so libraries don't have to do anything special to access the dynamic features of those classes.

java.classpath also provides a similar mechanism that the Immutant classloader implements as well. If you are more open to dependencies that are under org.clojure, using that will work as well. Ideally, I'd like to see java.classpath subsume dynapath.

Comment by James Cash [ 19/Jun/14 4:23 PM ]

Made a new patch that sidesteps the all-classpath-urls issue by just using io/resource instead of iterating over all urls

Comment by David Nolen [ 01/Jul/14 9:26 PM ]

Can people chime in whether the patch works for them, thanks.





[CLJS-1125] Simple corrupted compiled file detection Created: 16/Mar/15  Updated: 05/Aug/15

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

Type: Enhancement Priority: Major
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-1228] cljs.util/topo-sort is polynomial on larger dependency graphs Created: 28/Apr/15  Updated: 05/Aug/15

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

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


 Comments   
Comment by David Nolen [ 08/May/15 7:35 AM ]

This problem was reported due the performance of cljs.analyzer/ns-dependents when used by cljs.compiler/compile-file. However as cljs.compiler/compile-file does not need the topological sorting bit, we should supply a faster alternative to cljs.analyzer/ns-dependents which simply returns a distinct seq which can be poured into a set.





[CLJS-1236] `constructor` needs to munged if used as namespace segment Created: 01/May/15  Updated: 05/Aug/15

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

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


 Description   

Will make goog.require believe that the namespace has already been loaded and throw an exception since all JS objects have the constructor property.



 Comments   
Comment by David Nolen [ 04/May/15 8:19 AM ]

This issue is not quite as simple as it sounds as we actually need a special case for namespace munging. For example we don't want to munge Foo.constructor but we do want to munge ns.foo.constructor. To implement the fix correctly it would require changing all the cases where library names are munged.





[CLJS-1237] ns-unmap doesn't work on refers from cljs.core Created: 01/May/15  Updated: 05/Aug/15

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

Type: Defect Priority: Major
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-1300] REPLs do no write out updated deps.js when compiling files Created: 05/Jun/15  Updated: 05/Aug/15

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

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


 Description   

For example a user may edit a file including a new dependency. This will work at the REPL but if a browser refresh is made the emitted goog.require will fail due to the initial deps.js file being stale.






[CLJS-1328] Support defrecord reader tags Created: 04/Jul/15  Updated: 05/Aug/15

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

Type: Enhancement Priority: Major
Reporter: Herwig Hochleitner Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: reader, readertags


 Description   

Currently, defrecord instances print similar to how they do in clojure

> (pr-str (garden.units/px 5))
#garden.types.CSSUnit{:unit :px, :magnitude 5}

This representation cannot be read by the compiler, nor at runtime by cljs.reader/read-string

> #garden.types.CSSUnit{:unit :px, :magnitude 5}
clojure.lang.ExceptionInfo: garden.types.CSSUnit {:type :reader-exception, :line 1, :column 22, :file "NO_SOURCE_FILE"}
...
> (cljs.reader/read-string "#garden.types.CSSUnit{:unit :px, :magnitude 5}")
#<Error: Could not find tag parser for garden.types.CSSUnit in ("inst" "uuid" "queue" "js")>
...

Analysis

The two requirements - using record literals in cljs source code and supporting runtime reading - can be addressed by using the analyzer to find defrecords and registering them with the two respective reader libraries.

Record literals

Since clojurescript reads and compiles a file at a time, clojure's behavior for literals is hard to exactly mimic. That is, to be able to use the literal in the same file where the record is defined.
A reasonable compromise might be to update the record tag table after each file has been analyzed. Thus the literal form of a record could be used only in requiring files.

EDIT: Record literals can also go into the constant pool

cljs.reader

To play well with minification, the ^:export annotation could be reused on defrecords, to publish the corresponding reader tag to cljs.reader.

Related Tickets



 Comments   
Comment by David Nolen [ 08/Jul/15 12:00 PM ]

It's preferred that we avoid exporting. Instead we can adopt the same approach as the constant literal optimization for keywords under advanced optimizations. We can make a lookup table (which won't pollute the global namespace like exporting does) which maps a string to its type.

I'm all for this enhancement.





[CLJS-1332] investigate inlining version of cljs.core/get Created: 10/Jul/15  Updated: 05/Aug/15

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

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





[CLJS-1350] Compiler support for browser REPL Created: 19/Jul/15  Updated: 05/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None


 Description   

Currently the browser REPL experience could be considerably enhanced just by eliminating manual configuration in source. Instead REPL configuration could happen via a compiler option. This would make REPL support considerably more robust in the face of user errors while developing.






[CLJS-1373] Generalize CLJS-1324, check invokes of all IFn implementors Created: 28/Jul/15  Updated: 05/Aug/15

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

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


 Description   

We currently track all IFn implementors but in order to do arity checking of statically analyzeable invokes of keywords, vector, etc. we need to do a bit more. extend-type should update the type in the compiler state with :method-params :max-fixed-arity and :variadic. Then we can just reuse the existing checks in cljs.analyzer/parse-invoke.






[CLJS-349] cljs.compiler: No defmethod for emit-constant clojure.lang.LazySeq Created: 30/Jul/12  Updated: 05/Aug/15

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

Type: Defect Priority: Minor
Reporter: Julien Fantin Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: Text File 0001-CLJS-349-Allow-ISeq-to-be-emitted-by-macros-as-a-con.patch     File fixbug349.diff    

 Description   

The cljs compiler errors when trying to emit-constant for a clojure.lang.LazySeq.

Example : https://www.refheap.com/paste/3901

Here syms is defined as a LazySeq on line 3, then on line 7 it is quoted. The error is included in the refheap.

Emitting a cljs.core.list for this type seems to solve the issue.



 Comments   
Comment by David Nolen [ 31/Aug/12 9:27 AM ]

Can you identify precisely where a LazySeq is getting emitted here? A LazySeq is not literal so this seems like a bug in the macro to me. I could be wrong. Thanks!

Comment by Herwig Hochleitner [ 28/Oct/12 9:31 PM ]

The lazy seq seems to be introduced on line 7, the '~syms form

`(let [mappings# (into {} (map-indexed #(identity [%2 %1]) '~syms))

Clojure allows lazy-seqs to be embedded: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L4538

As an aside: The relevant protocol is not literality, but the print-dup multimethod. Do / Should we have print-dup in CLJS?

Comment by Herwig Hochleitner [ 31/Oct/12 10:10 PM ]

Attached patch 0001 doesn't add a case for LazySeq, but folds two cases for PersistentList and Cons into one for ISeq.

Comment by David Nolen [ 19/Nov/13 9:28 PM ]

This approach seems acceptable but this is an old patch can we update for master?





[CLJS-1238] Setting *main-cli-fn* when using :target :nodejs shouldn't be manditory Created: 01/May/15  Updated: 05/Aug/15

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

Type: Enhancement Priority: Minor
Reporter: Jeremy Shoemaker Assignee: Unassigned
Resolution: Unresolved Votes: 1
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.





[CLJS-1286] REPL environment should be able to provide advice if source mapping fails Created: 23/May/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-871] .-default property access returns nil Created: 11/Oct/14  Updated: 05/Aug/15

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

Type: Defect Priority: Major
Reporter: Joel Holdbrooks Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: None

Attachments: Text File 871.patch     Text File 871.patch    
Patch: Code and Test

 Description   

Types defined with deftype/defrecord which have a default field will incorrectly return nil with property access. The following example will return nil.

(deftype Foo [default])

(let [foo (Foo. "bar")]
  (.-default foo))


 Comments   
Comment by Joel Holdbrooks [ 13/Oct/14 4:19 PM ]

Patch attached. I should point out that I had to borrow js-reserved from the compiler namespace and the warning message provided hard codes the munged symbol information instead of reusing the compiler's munge fn.

Comment by Joel Holdbrooks [ 13/Oct/14 9:41 PM ]

For the sake of history, I should provide more context to this patch (I'm unable to edit the issue title for some reason). It isn't just .-default it is any field name that is also a JavaScript identifier (eg. public, private, if).

Comment by David Nolen [ 14/Oct/14 5:26 AM ]

Please lift js-reserved and any helpers like munge into the shared namespace cljs.util so that logic an be shared and hard coding avoided. Thanks.

Comment by Joel Holdbrooks [ 14/Oct/14 5:03 PM ]

Are you sure, David? That might make this patch a bit more noisy. If it's not a problem I'm happy to do it.

Comment by David Nolen [ 14/Oct/14 6:06 PM ]

I'm sure, I'd like to avoid this kind of code duping. Cleaner in the end and better moving forward.

Comment by Joel Holdbrooks [ 18/Mar/15 11:43 AM ]

Updated to use new refactorings

Comment by David Nolen [ 18/Mar/15 11:46 AM ]

The warning is not desirable. Instead we should just munge and ensure property access always works.





[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: 05/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
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-1194] data_readers.cljc Created: 10/Apr/15  Updated: 05/Aug/15

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

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


 Description   

Now that conditional reading has landed we can implement support for data_readers.cljc to get both compile time and runtime support.



 Comments   
Comment by David Nolen [ 10/Apr/15 7:45 PM ]

This needs http://dev.clojure.org/jira/browse/CLJ-1699 to be useful.

Comment by Nikita Prokopov [ 19/May/15 7:58 AM ]

CLJ-1699 has landed.

Right now CLJS tries to compile data_readers.cljc as a regular source code file:

Exception in thread "main" java.lang.AssertionError: No ns form found in src/data_readers.cljc, compiling:(/private/var/folders/0h/9vv4g3d955l6ctwwl4k9xjy40000gn/T/form-init3533791126017861878.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)
Comment by David Nolen [ 19/May/15 8:53 AM ]

This should be addressed first: http://dev.clojure.org/jira/browse/CLJS-1277





[CLJS-1280] double analysis warnings when using cljs.repl/load-file special fn Created: 21/May/15  Updated: 05/Aug/15

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

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





[CLJS-1298] source-on-disk conditional should include :source-url Created: 05/Jun/15  Updated: 05/Aug/15

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

Type: Defect Priority: Major
Reporter: Bruce Hauman Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Environment:

All



 Description   

cljs.closure/source-on-disk is conditional on having the IJavaScript having a :source-url. Currently this conditional is exercised after the call to util/ext. But util/ext has a precondition that will fail on a nil :source-url.

Solution: protect util/ext by checking for :source-url before calling util/ext.

(defn source-on-disk
  "Ensure that the given IJavaScript exists on disk in the output directory.
  Return updated IJavaScript with the new location if necessary."
  [opts js]
  (if (write-js? js)
    (write-javascript opts js)
    ;; always copy original ClojureScript sources to the output directory
    ;; when source maps enabled
    (let [out-file (when-let [ns (and (:source-map opts)
                                      (:source-url js) ;;<--- ADD THIS HERE
                                      (first (:provides js)))]
                     (io/file (io/file (util/output-directory opts))
                       (util/ns->relpath ns (util/ext (:source-url js)))))
          source-url (:source-url js)]
      (when (and out-file source-url
                 (or (not (.exists ^File out-file))
                     (> (.lastModified (io/file source-url))
                        (.lastModified out-file))))
        (spit out-file (slurp source-url)))
      js)))

Or some refactoring of the above.



 Comments   
Comment by David Nolen [ 05/Jun/15 2:20 PM ]

In what cases will an IJavaScript not have a :source-url? Just trying to get some context.

Comment by Bruce Hauman [ 05/Jun/15 2:42 PM ]

Good question. When the sources are already output javascript and they have an output dir relative :url but not a :source-url, this doesn't seem like it should happen.

I'm getting a situation like this

:url #object[java.net.URL 0x5f921081 "file:/Users/brucehauman/workspace/temp/hello-world/resources/goog/uri/utils.js"]
:source-url nil

Comment by David Nolen [ 05/Jun/15 2:58 PM ]

Ok that seems like the real underlying issue. Do you have a small reproducer by chance?

Comment by Bruce Hauman [ 05/Jun/15 3:08 PM ]

It seems like its when the :asset-path is "". And :output-dir is at the root of a resource path like :output-dir "resources".





[CLJS-1343] Investigate performance impact of using records over maps during compilation Created: 17/Jul/15  Updated: 05/Aug/15

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

Type: Task Priority: Major
Reporter: David Nolen Assignee: Sebastian Bensusan
Resolution: Unresolved Votes: 0
Labels: None


 Description   

Profiles in Chrome DevTools reveal that a considerable amount of time is spent in PersistentArrayMap and PersistentHashMap. It would be worth considering coverting all maps into records with a protocol to extract the pure data form of the AST.






[CLJS-434] ClojureScript compiler prepends "self__" to defmulti forms when metadata in form of ^:field. Created: 01/Dec/12  Updated: 05/Aug/15

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

Type: Defect Priority: Minor
Reporter: Andrew Mcveigh Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug
Environment:

Mac OS X (10.7), java version "1.6.0_37", leiningen 2 preview 10, cljsbuild 0.2.9.
clojure/clojurescript master 01 December 2012 - 5ac1503



 Description   

Using the def form, with the specific metadata ^:field causes the cljs compiler
to prepend "self__" to the output js form.

The browser (latest chrome/firefox) does not recognize "self__".

Test Case: Tested against master: 5ac1503
-------------

(ns test-def)

(def ^:foo e identity)
e
; test_def.e = cljs.core.identity;
; test_def.e;

(def ^:field f identity)
f
; test_def.f = cljs.core.identity;
; self__.test_def.f;
; Uncaught ReferenceError: self__ is not defined

https://gist.github.com/4185793



 Comments   
Comment by Brandon Bloom [ 01/Dec/12 5:37 PM ]

code tags

Comment by David Nolen [ 20/Jan/13 12:54 AM ]

This one is a bit annoying. We should probably use namespaced keywords internally.





[CLJS-620] Warnings are generated when using a macro in argument position Created: 14/Oct/13  Updated: 05/Aug/15

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

Type: Defect Priority: Minor
Reporter: Julien Eluard Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None

Attachments: File CLJS-620.diff    

 Description   

Using a macro in argument position (e.g. (map macro [])) generates a warning:
WARNING: Use of undeclared Var test/node at line 4 src/test.cljs

Find a reproduction project here.



 Comments   
Comment by Jozef Wagner [ 15/Oct/13 3:30 AM ]

and what would you like, a better warning? Clojurescript allows same name for macro and for function, so you can both have macro + and fn +. Macro version will be used when is first in the list, fn version otherwise.

Comment by Jonas Enlund [ 15/Oct/13 3:38 AM ]

For reference, Clojure generates the following error message:

user=> (map when [])
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'clojure.core/when, compiling:(NO_SOURCE_PATH:1:1)

The "obvious" approach would be to add

(when-let [m (get-expander sym env)]
  (throw (error env (str "Can’t take value of a macro: " m))))

to resolve-var[1]. Unfortunately this doesn’t work in ClojureScript due to the way inlining works. A simple workaround is to add {:inline true} metadata to macros that are later redefined as functions in core.cljs and check for invalid macro usage like this:

(when-let [m (get-expander sym env)]
  (and (-> m meta :inline not)    
       (throw (error env (str "Can’t take value of a macro: " m)))))

Another approach would perhaps be to rethink how inlining works as it seems kind of brittle to have macros in cljs/core.clj with the same name as functions in cljs/core.cljs (especially since both namespaces are auto-imported. Is cljs.core/inc a function, a macro, or both?). Maybe there’s a better way?

[1] https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/analyzer.clj#L193

Comment by Julien Eluard [ 15/Oct/13 6:23 AM ]

My bad, didn't realize it didn't make sense. Of course it's obvious now. I was confused by recent changes around function/macro name validation.
Now the warning could probably be improved a little but that doesn't seem very important. The Clojure warning is not that much better.

Comment by David Nolen [ 15/Oct/13 11:58 AM ]

We're not going to change how inlining works - the whole point is that we can reuse the same names. Adding :inline metadata seems like a promising way to warn of incorrect usage of inlining macros.





[CLJS-744] ISequential types should implement JS indexOf, lastIndexOf Created: 05/Jan/14  Updated: 05/Aug/15

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

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





[CLJS-1271] Missing warning when assigning namespaces via def Created: 17/May/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1327] Support Transit JSON for analysis caches Created: 03/Jul/15  Updated: 05/Aug/15

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

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


 Description   

A significant amount of time is spent reading caches both during cold builds and REPL start. Switching to Transit for the analysis cache format should deliver a very large performance boost. Having a direct dep on Transit is undesirable since it is a critical part of existing applications. Instead we could allow users to provide analysis cache encoder/decoder functions to the compiler options. This does mean we cannot use the AoTed analysis for cljs.core if these are provided.






[CLJS-1139] Repeated applications of `ns` form at the REPL are not additive Created: 17/Mar/15  Updated: 05/Aug/15

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

Type: Defect Priority: Major
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-1141] memoization of js-dependency-index and get-upstream-deps needs knobs Created: 18/Mar/15  Updated: 05/Aug/15

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

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

Attachments: Text File CLJS_1141.patch     Text File CLJS-1141-with-js-dep-caching-latest.patch    

 Description   

knobs should be exposed for more dynamic compilation environments like Figwheel which may desire to add dependencies to the classpath on the fly.



 Comments   
Comment by Bruce Hauman [ 21/Mar/15 3:51 PM ]

A patch that caches upstream dependencies in the compiler env.

Comment by Bruce Hauman [ 21/Mar/15 3:59 PM ]

Actually I'm going to submit another patch that includes the memoize calls in js-deps.

Comment by Bruce Hauman [ 28/Mar/15 12:50 PM ]

New patch that moves cljs.js-deps memoization to current env/compiler as well as get-upstream-deps.

Unfortunately there is a circular dep between cljs.env and cljs.js-deps, if we want to cache in env/compiler. I overcame this with a resolve.

Compile performance is either completely unchanged or slightly improved based on several test runs.

Comment by Bruce Hauman [ 28/Mar/15 2:22 PM ]

Hold off on this. Its not behaving as expected. Doesn't seem to be caching in certain situations.

Comment by David Nolen [ 28/Mar/15 2:26 PM ]

Thanks for the update. This will definitely not land until after the pending REPL/piggieback release anyhow.

Comment by Bruce Hauman [ 28/Mar/15 2:44 PM ]

Yeah there is an obvious bug and a subtle one. Hopefully will finish it up soonish.

Comment by Bruce Hauman [ 28/Mar/15 3:43 PM ]

Alright, this latest patch works. There was a subtle memoizing nil value bug.





[CLJS-1147] reconnect logic for browser REPLs Created: 18/Mar/15  Updated: 05/Aug/15

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

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


 Description   

Instead of forcing users to refresh browser and lose application state, the browser REPL should poll once a second to connect if connection is unreachable for some reason.



 Comments   
Comment by David Nolen [ 21/Mar/15 8:56 PM ]

This is firmly a major nice-to-have, but not a blocker.





[CLJS-1174] Simple warning if a namespace with dashes not found but a file path with dashes exists Created: 27/Mar/15  Updated: 05/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: easy





[CLJS-1207] Emit a warning if multiple resources found for a ClojureScript namespace Created: 15/Apr/15  Updated: 05/Aug/15

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

Type: Enhancement Priority: Major
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-1277] relax requirement that files must declare a namespace, default to cljs.user Created: 19/May/15  Updated: 05/Aug/15

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

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


 Description   

This aligns better with Clojure itself supports.



 Comments   
Comment by David Nolen [ 14/Jun/15 10:30 AM ]

There are a few hurdles in order to make progress on this ticket. The first is that in order to be useful something like require etc. outside the ns needs to be supported in order to be useful.

Comment by Jonathan Boston [ 18/Jul/15 12:17 PM ]

Needs http://dev.clojure.org/jira/browse/CLJS-1346 to be useful.





[CLJS-1315] Warning on Google Closure enum property access with / Created: 18/Jun/15  Updated: 05/Aug/15

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

Type: Defect Priority: Major
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-375] loop doesn't seem to preserve tag information as evidenced by extra cljs.core.truth_ calls Created: 06/Sep/12  Updated: 05/Aug/15

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

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





[CLJS-485] clojure.string/replace ignores regex flags Created: 12/Mar/13  Updated: 05/Aug/15

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

Type: Defect Priority: Minor
Reporter: Esa Virtanen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug, patch, test

Attachments: Text File 0001-Take-regex-flags-m-i-into-account-in-clojure.string-.patch    
Patch: Code and Test

 Description   

The replace function in namespace clojure.string ignores regex flag provided in the match pattern. For example:

CLJS
clojure.string/replace "I am NOT matched" #"(?i)not " "")
=> "I am NOT matched"
CLJ
clojure.string/replace "I am NOT matched" #"(?i)not " "")
=> "I am matched"

The attached patch fixes this by parsing the m and i flags, if set, from the match object, instead of explicitly setting only "g".



 Comments   
Comment by Chas Emerick [ 19/Mar/14 9:29 AM ]

I can confirm the bug. The attached patch applies cleanly, and works as expected.

Esa, sorry for the long delay (this one must have slipped through the cracks)! Could you please submit a contributor's agreement, so that your patch can be merged? More info is here:

http://clojure.org/contributing

Comment by lvh [ 26/Jul/15 5:56 PM ]

I got bit by this bug. Working on figuring out if I can sign that agreement.

Comment by lvh [ 27/Jul/15 11:55 AM ]

This is a duplicate of CLJS-794.





[CLJS-1070] top-level boolean inference does not work Created: 28/Feb/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1128] Describe internationalization strategies via Google Closure on the wiki Created: 16/Mar/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1129] :modules tutorial for wiki Created: 16/Mar/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1195] generic reusable command line argument parsing for REPLs Created: 10/Apr/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1222] Sequence of a stateful transducer is producing the wrong answer Created: 24/Apr/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-719] this-as behaves incorrectly in "scoping function" Created: 07/Dec/13  Updated: 05/Aug/15

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

Type: Defect Priority: Major
Reporter: Kevin Marolt Assignee: David Nolen
Resolution: Unresolved Votes: 1
Labels: None


 Description   

When a this-as expression gets put in a "scoping function", e.g. in a let-binding, the value bound via this-as refers to the scoping function, and not to the outer scope.

Example:

(def foo
  (js-obj
    "bar" "baz"
    "getBarRight" (fn [] (this-as self (.-bar self)))
    "getBarWrong" (fn []
                    (let [bar (this-as self (.-bar self))]
                      bar))))
     
(.log js/console (.getBarRight foo)) ;; => "baz"
(.log js/console (.getBarWrong foo)) ;; => undefined

Whereas foo.getBarRight expands to something like

function() {
  var self = this; // this refers to foo
  return self.bar; // returns "bar"
}

foo.getBarWrong on the other hand expands to

function() {
  var bar = function() {
    var self = this; // this refers to enclosing function
    return self.bar; // returns undefined
  }();
  return bar; // returns undefined
}





[CLJS-968] Metadata on function literal inside of a let produces invalid Javascript Created: 07/Jan/15  Updated: 05/Aug/15

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

Type: Defect Priority: Major
Reporter: Bobby Eickhoff Assignee: David Nolen
Resolution: Unresolved Votes: 1
Labels: bug
Environment:

Originally found with [org.clojure/clojurescript "0.0-2496"]
Still reproducible with the latest cljsc (b5e9a5116259fc9f201bee4b9c6564f35306f9a5)



 Description   

Here is a minimal test case that produces the invalid Javascript:

(defn f []
  (let [a 0]
    ^{"meta" "data"}
    (fn [] true)))

The compiled Javascript includes the invalid token sequence "return return". (Per Chrome: Uncaught SyntaxError: Unexpected token return)

The problem does not occur if the metadata applies to a map literal instead of a function literal.
The problem only occurs when the function and metadata are inside of a let.



 Comments   
Comment by Bobby Eickhoff [ 07/Jan/15 9:45 PM ]

I forgot to try with-meta. Using with-meta does not produce this syntax error, so it's only a problem with the reader macro for metadata.

Comment by David Nolen [ 08/Jan/15 7:41 AM ]

Any quick thoughts about this one Nicola? Quite possibly a compiler issue on the CLJS side.

Comment by Nicola Mometto [ 08/Jan/15 8:07 AM ]

David, I understand why this happens but I don't know enough about how cljs's js emission to propose a fix.
The issue is that with this commit: https://github.com/clojure/clojurescript/commit/d54defd32d6c5ffcf6b0698072184fe8ccecc93a the following scenario is possible:

{:op :meta
 :env {:context :return}
 :expr {:op :fn
        :env {:context :expr}
        :methods [{:op :fn-method 
                   :env {:context :return} ..}]
        ..}
 ..}

i.e. analyze-wrap-meta changes the context of the :fn node to :expr but keeps the context of the :fn-methods to :return.

This causes both
https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/compiler.clj#L575-L576
and
https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/compiler.clj#L488 (https://github.com/clojure/clojurescript/blob/master/src/clj/cljs/compiler.clj#L233)

to be true and emit a "return".

Comment by David Nolen [ 06/May/15 7:15 PM ]

Hrm, it appears analyze-wrap-meta may need to defer to a helper to change the :context of the given AST node.





[CLJS-1127] validate compiled file written to disk Created: 16/Mar/15  Updated: 05/Aug/15

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

Type: Enhancement Priority: Major
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-794] RegExp flags are being dropped by `string/replace` Created: 09/Apr/14  Updated: 05/Aug/15

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

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


 Description   

`clojure.string/replace` accepts either a string or pattern argument to match against.

For pattern arguments, the current implementation discards the original RegExp and creates a new one:
`(.replace s (js/RegExp. (.-source match) "g") replacement)`

This is killing any flags on the original pattern (case insensitivity, for example). As a result, things like `(str/replace "Foo" #"(?i)foo" "bar")` currently fail. The result is "Foo", it should be "bar".

Can I submit a patch that'll check for and preserve other (i/m/y) flags?

Thanks



 Comments   
Comment by David Nolen [ 02/Dec/14 5:42 AM ]

A patch is welcome for this. Thanks.

Comment by lvh [ 27/Jul/15 11:55 AM ]

This appears to be identical to CLJS-485, which has a patch (by someone who hasn't signed the CLA yet).





[CLJS-1133] REPL require results in warnings to be emitted twice Created: 17/Mar/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1136] Initial require fails to fully load added symbols Created: 17/Mar/15  Updated: 05/Aug/15

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.48
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-1392] cljs.repl/source regression Created: 05/Aug/15  Updated: 05/Aug/15  Resolved: 05/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Description   

cljs.repl/source no longer works in the builtin REPLs.



 Comments   
Comment by David Nolen [ 05/Aug/15 5:39 PM ]

fixed https://github.com/clojure/clojurescript/commit/18d2cce8fabfc90732f7502ba3460fe168d3f2bc





[CLJS-1391] Error when building for target :nodejs Created: 04/Aug/15  Updated: 05/Aug/15  Resolved: 05/Aug/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: GSoC
Fix Version/s: GSoC

Type: Defect Priority: Major
Reporter: Maria Geller Assignee: Maria Geller
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File CLJS-1391.patch    

 Description   

I introduced this one with the last patch for js transforms (CLJS-1177). I get the following error when building for target nodejs:

Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.Associative
at clojure.lang.RT.assoc(RT.java:778)
at clojure.core$assoc__4142.invoke(core.clj:191)
at cljs.closure$write_javascript.invoke(closure.clj:1353)



 Comments   
Comment by Maria Geller [ 04/Aug/15 1:58 PM ]

Attached patch for this one. When writing out javascript, check for map before assigning javascript source to :source key.

Comment by David Nolen [ 05/Aug/15 10:59 AM ]

fixed https://github.com/clojure/clojurescript





[CLJS-1389] Support Override of Custom Closure Defines w/ :none/:whitespace Created: 02/Aug/15  Updated: 04/Aug/15  Resolved: 03/Aug/15

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

Type: Enhancement Priority: Minor
Reporter: Martin Klepsch Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File CLJS-1389-provide-define-macro-to-support-proper-use.patch     Text File CLJS-1389-provide-define-macro-to-support-proper-use.patch    

 Description   

When creating "uncompiled" builds (:none, :whitespace) it is not possible to override a custom Closure @define by passing a different value to the compiler option :closure-defines.

Closure provides CLOSURE_UNCOMPILED_DEFINES and CLOSURE_DEFINES [1] which can be set before loading any other code.
Overriding defines this way only works for Defines with done with goog.define.
Using goog.define however introduces other problems:
1) The name passed to goog/define must match the name in compiled JS. E.g. (goog/define "hello_world.core.DBG" true).
2) This produces no var in CLJS which causes an undeclared var warning if the defined var is used.
3) There is nothing to be annotated causing ERROR: JSC_INVALID_MISSING_DEFINE_ANNOTATION.

Getting the CLOSURE_UNCOMPILED_DEFINES into the build is straightforward, just not sure how to deal with those other complexities.

[1] http://google.github.io/closure-library/api/source/closure/goog/base.js.src.html#l147



 Comments   
Comment by David Nolen [ 03/Aug/15 5:14 AM ]

Why can't we just emit CLOSURE_DEFINES under :none? If user supplies :main this is simple to do here https://github.com/clojure/clojurescript/blob/d946a58153ec5132063f1ac29acd8d4bcd1bdeb2/src/main/clojure/cljs/closure.clj#L1124-L1143

If this cannot work there should be documentation on this ticket as to why. Thanks!

Comment by Martin Klepsch [ 03/Aug/15 6:06 AM ]

I created a sample in the Clojurescript repo to illustrate this: https://github.com/martinklepsch/clojurescript/tree/defines-in-none/samples/defines

TLDR; goog.define is not just sugar and is required to look things up at runtime from CLOSURE_DEFINES and CLOSURE_UNCOMPILED_DEFINES.

Copy of the README:

Custom Defines in :none

See CLJS-1389.

Most straightforward solution would be to prepend CLOSURE_UNCOMPILED_DEFINES or CLOSURE_DEFINES to the main file as
done in 9962889. Unfortunately
this won't work. Values in these globals are only looked up at runtime when something has been defined with

/** @define {boolean} */
goog.define('my.thing.DEBUG', false)

See source of goog.define.
In comparison defining something through CLJS will result in the following

(def ^{:jsdoc ["@define {boolean}"]}
  DEBUG true)

compiles to:

/**
 * @define {boolean}
 */
my.thing.DEBUG = true;

The @define JSDoc tag is appropriately set but since there is no runtime lookup no values from CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES will be used.
In comparison goog.define does the runtime lookup.

There is an example in this directory where you can see that modifying the :closure-defines passed to the compiler will not have any effect on the compiled core.js.

Docstrings for CLOSURE_DEFINES and CLOSURE_UNCOMPILED_DEFINES are also interesting.

Possible solution

Using goog.define instead of just the :jsdoc metadata would allow
overriding in all optimization modes. Emitting goog.define properly without
causing warnings or errors requires the following:

1. The goog.define call needs to be annotated with the JSDoc @define tag.
2. We need to declare the var defined, otherwise there will be undeclared Var warnings

There's a macro in samples/defines/src/hello_world/core.clj that I
wrote while experimenting what's required to make this work.

Comment by Martin Klepsch [ 03/Aug/15 11:19 AM ]

Patch introducing cljs.core/define macro and prepending :closure-defines to main file.

EDIT: I settled on using CLOSURE_UNCOMPILED_DEFINES because it seemed most appropriate given
that it's only ever used in "uncompiled" mode.

Comment by Martin Klepsch [ 03/Aug/15 12:16 PM ]

Please review/give feedback.

Comment by David Nolen [ 03/Aug/15 1:49 PM ]

This patch needs to be rebased to master.

Comment by Martin Klepsch [ 03/Aug/15 1:53 PM ]

rebased patch on master

Comment by David Nolen [ 03/Aug/15 2:01 PM ]

fixed https://github.com/clojure/clojurescript/commit/5ef5f10ab87b8cafbb13ce5b55e81d5b4fd655d9

Comment by Martin Klepsch [ 04/Aug/15 8:20 AM ]

cljs.core/define has been renamed to cljs.core/goog-define.
https://github.com/clojure/clojurescript/commit/37ce674742f2969597cbb3b5e25dfa0cc77bd619





[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-1177] A compiler support for non-Closure transforms (JSX, etc) Created: 28/Mar/15  Updated: 03/Aug/15  Resolved: 03/Aug/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3126
Fix Version/s: GSoC

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: Maria Geller
Resolution: Completed Votes: 1
Labels: None

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

 Description   

There are now a variety of JS dialects, JSX, TypeScript etc. which need to be transformed first into regular JS before module processing. We should devise a standard way to deal with this. As we cannot predict what various dialects may arise in the future it's probably best to provide something like :preprocess-module option to cljs.closure/build.



 Comments   
Comment by David Nolen [ 17/Jun/15 5:20 PM ]

We should verify with Babel now that React is moving away from JSX.

Comment by David Nolen [ 01/Jul/15 6:02 PM ]

I went ahead and looked into this a bit. Looks like getting Babel to work will take some time. Babel has difficulties loading into Nashorn due to code size limits that originate from Lodash usage. JSX while deprecated is still the most widely used option so we have some time yet and the interface supplied by both tools is exactly the same. So we should test this feature with JSX and switch to Babel when the issues are resolved.

Comment by Maria Geller [ 03/Aug/15 12:38 PM ]

Attached a patch for adding support for foreign libs preprocessing. Users can add support for a JS dialect by implementing the js-transforms function and using the :preprocess foreign lib compiler option. Both, js-transforms and convert-js-modules, now take an IJavaScript and return an IJavaScript.

Comment by David Nolen [ 03/Aug/15 1:54 PM ]

fixed https://github.com/clojure/clojurescript/commit/b7309fdf009fe5c6f1302ab65ef407497d9e046b





[CLJS-1296] browser REPL should queue prints before connection then flush after connection Created: 01/Jun/15  Updated: 03/Aug/15  Resolved: 03/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Description   

If the page loads and attempts to print before the connection is made the application will unintuitively error out.



 Comments   
Comment by David Nolen [ 03/Aug/15 6:55 AM ]

fixed https://github.com/clojure/clojurescript/commit/fb12e78321e741226e7c65c8eb188dd8def24f9e





[CLJS-1388] Stacktrace element handling for :output-dir w/o file/line/column Created: 02/Aug/15  Updated: 03/Aug/15  Resolved: 03/Aug/15

Status: Resolved
Project: ClojureScript
Component/s: None
Affects Version/s: 1.7.28
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: None


 Description   

If parsing stacktraces when :host and :port are supplied, elements that have no file/line/column are properly accommodated.

For example:

https://github.com/clojure/clojurescript/blob/v1.7/src/main/cljs/cljs/stacktrace.cljc#L266

But, an attempt to process a similar stacktrace when :output-dir is being used, an error will be thrown. (This can occur when using :output-dir for embedded JavaScriptCore.)

Here is a simple repro:

(parse-stacktrace {}
"cljs$core$seq@out/cljs/core.js:3999:17
cljs$core$first@out/core.js:4018:22
cljs$core$ffirst@out/cljs/core.js:5161:39
global code"
{:ua-product :safari} 
{:output-dir "out"})


 Comments   
Comment by David Nolen [ 03/Aug/15 5:50 AM ]

fixed https://github.com/clojure/clojurescript/commit/49f3ea9c1e5064bacb3e13d29f5f3f18af1f4bc1





[CLJS-1311] Improve error reporting when converting JavaScript modules Created: 15/Jun/15  Updated: 02/Aug/15  Due: 21/Aug/15  Resolved: 02/Aug/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: GSoC
Fix Version/s: GSoC

Type: Enhancement Priority: Major
Reporter: Maria Geller Assignee: Maria Geller
Resolution: Completed Votes: 2
Labels: None

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

 Description   

When using the Google Closure compiler via the command line and trying to convert a JavaScript module which has trailing commas, the compiler throws a parse error, e.g.:

chance.js:1109: ERROR - Parse error. IE8 (and below) will parse trailing commas in array and object literals incorrectly. If you are targeting newer versions of JS, set the appropriate language_in option.
                        '09' + self.string({ pool: '0123456789', length: 8}),

At the moment, we don't throw any errors when converting a module and an error occurs. Instead, just the following gets emitted by the Google Closure compiler:

goog.provide("module$libs$chance");var module$libs$chance={}

We need to change this behaviour to show the warnings and errors the Google Closure compiler is emitting.



 Comments   
Comment by Kristian Mandrup [ 16/Jun/15 10:53 PM ]

Yes, by default don't convert the CommonJS module, unless perhaps if you set a specific --force compile option.

Comment by Maria Geller [ 01/Aug/15 3:34 PM ]

Show Google Closure compiler warnings when converting JS modules and set :closure-warnings, :closure-extra-annotations and :pretty-print options. We cannot use the make-options functions for this, since it requires an optimizations level. Instead pull options that we want to use into set-options function.

Comment by David Nolen [ 02/Aug/15 8:37 AM ]

fixed https://github.com/clojure/clojurescript/commit/d946a58153ec5132063f1ac29acd8d4bcd1bdeb2





[CLJS-1387] support for Closure libraries that conform to classpath conventions does not work for local libraries Created: 01/Aug/15  Updated: 01/Aug/15  Resolved: 01/Aug/15

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

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Description   

Only works for libs in JARs.



 Comments   
Comment by David Nolen [ 01/Aug/15 10:50 AM ]

fixed https://github.com/clojure/clojurescript/commit/f14b561640cd5da3aa52d5425943734635ee749e





[CLJS-1386] Symbols should be added to the constants table Created: 01/Aug/15  Updated: 01/Aug/15  Resolved: 01/Aug/15

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

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Description   

Now that optional bootstrap is a thing, this is a significant optimization.



 Comments   
Comment by David Nolen [ 01/Aug/15 6:11 AM ]

fixed https://github.com/clojure/clojurescript/commit/d92b3004046d1f83ab76a2b94f4129cb16e47848





[CLJS-1336] Create bootstrapping namespace Created: 12/Jul/15  Updated: 31/Jul/15  Resolved: 31/Jul/15

Status: Resolved
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3308
Fix Version/s: 1.7.28

Type: Enhancement Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Description   

This namespace should provide basic analyze, emit, eval functionality along with necessary helpers for establishing the compilation environment. *load-file* or something similar must be bound by the user. One open question is how to handle async versus sync file loading. In the browser context only the former is really realistic, while in Node.js/iOS/Android we have considerably more flexibility.



 Comments   
Comment by David Nolen [ 12/Jul/15 2:05 PM ]

CLJS-1337 must be addressed first.

Comment by David Nolen [ 31/Jul/15 8:12 PM ]

fixed in master





[CLJS-1385] cljs.js/compile-str does not correctly track the current ns Created: 31/Jul/15  Updated: 31/Jul/15  Resolved: 31/Jul/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3308
Fix Version/s: 1.7.28

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None


 Description   

Macros are not correctly expanded. Instead macro calls resolve to cljs.user.



 Comments   
Comment by David Nolen [ 31/Jul/15 10:22 AM ]

fixed https://github.com/clojure/clojurescript/commit/f3cf45d745af5e63500375d084dd0d7b19c40dac





[CLJS-1380] Hashing is broken when using iphone 4 Created: 30/Jul/15  Updated: 31/Jul/15  Resolved: 31/Jul/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3308
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Jean-Louis Giordano Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: bug
Environment:

iphone 4 running iOS 7.1.2



 Description   

On a physical iPhone 4 running iOS 7.1.2, when running the `hash` function too many times at the top level, the result becomes inconsistent.

This only happens when not using the safari webinspector.

I have setup a minimal failing spec here: https://github.com/Jell/iphone-bug

Instructions to reproduce in the README.

I must confess, this took me a very long time to debug! I hope someone can take it from here.



 Comments   
Comment by Jean-Louis Giordano [ 30/Jul/15 7:23 AM ]

With the help of my colleague, we narrowed it down further to this:

(ns iphone-bug.core)

(def yyyy (hash "yyyy"))

(dotimes [i 1000] (hash "yyyy"))

(js/alert (str "Before: " yyyy " After: " (hash "yyyy")))
Comment by Jean-Louis Giordano [ 30/Jul/15 7:36 AM ]

Note: the bug only appears on iPhone 4, not iPhone 4S!

Comment by David Nolen [ 30/Jul/15 10:58 AM ]

I believe this is something to do with 32bit vs. 64bit JavaScriptCore. Super low priority but happy to take a patch if someone can devise one.

Comment by Thomas Heller [ 30/Jul/15 11:34 AM ]

Sounds like something which was fixed a long time ago and I haven't personally seen since. Maybe a recent change reverted the Math.imul fix?

http://dev.clojure.org/jira/browse/CLJS-839
http://dev.clojure.org/jira/browse/CLJS-830

Comment by David Nolen [ 30/Jul/15 11:53 AM ]

Thomas I'm not sure we solved this for older phones only relatively newer ones with specific versions of Safari. iPhone 4 was ARM Cortex A8, and the 4S was an ARM Cortex A9. Turns out both are 32bit, but I still suspect this is a JITing issue if it disappears with a debugger attached.

Comment by Thomas Heller [ 31/Jul/15 3:33 AM ]

Just saying that the description exactly matched what I saw when investigating the keyword hashing issue. The problem never appeared with the devtools running and sometimes produced the correct hash without. After adding the imul.js the issue disappeared and clients stopped sending invalid maps. See CLJS-830 for a list of user agents [1] that were problematic before, the issue was with a specific webkit version and seemingly not hardware related (iOS 7.1.2 was the version at the time, user agents don't identify the device though).

Not claiming that this is the same issue, just saying that it sounds very very similar.

[1] https://gist.github.com/thheller/8cae79cabd9ac74958ca

Comment by Jean-Louis Giordano [ 31/Jul/15 9:32 AM ]

We found a fix that works for us!

We redefine the imul polyfill (cljs/imul.js) with the following:

if(typeof Math.imul == "undefined" || (Math.imul(0xffffffff,5) == 0)) {
    Math.imul = function (a, b) {
      try {
        var ah  = (a >>> 16) & 0xffff;
        var al = a & 0xffff;
        var bh  = (b >>> 16) & 0xffff;
        var bl = b & 0xffff;
        // the shift by 0 fixes the sign on the high part
        // the final |0 converts the unsigned value into a signed value
        return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0)|0);
      } catch(err) { throw err; }
    }
}

The error is not really coming from inside this function, but adding the try/catch disables JIT optimisation for callers of imul as far as we understand, which solves the hashing issues we saw on Iphone 4.

The errors seems to come from bit operations, in particular the `x|0` operations (corresponding to (int x) calls or bit-or in CLJS).

Infuriating to pin the bug down, because observing intermediate variables will prevent the bug from happening!

Comment by Francis Avila [ 31/Jul/15 9:59 AM ]

This is going to cause an enormous performance regression.

Comment by Jean-Louis Giordano [ 31/Jul/15 10:05 AM ]

Well a perf regression for the browsers that do not have a built-in or broken Math/imul, which might not be that big of a deal?

This is good enough for us at least, so we will probably not be investigating this further.

Comment by David Nolen [ 31/Jul/15 10:20 AM ]

Thanks for the bug report good to have monkey-path solution for those that need it. Closing this one.





[CLJS-1384] cljs.js/eval-str doc that :ns returned upon success Created: 31/Jul/15  Updated: 31/Jul/15  Resolved: 31/Jul/15

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

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


 Description   

If eval-str is called and it succeeds with a `:value`, the resulting `:ns` is also passed back, but the doctoring hasn't been updated to reflect this fact.



 Comments   
Comment by David Nolen [ 31/Jul/15 8:38 AM ]

fixed https://github.com/clojure/clojurescript/commit/86f8255d4536e4e2022adb467db46293c7616f74





[CLJS-1381] cljs.js: Memory perf on JSC for first defn evaluation Created: 30/Jul/15  Updated: 30/Jul/15  Resolved: 30/Jul/15

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

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Duplicate Votes: 0
Labels: None

Attachments: PNG File memory.png    

 Description   

If you evaluate something like (defn f []) via cljs.js/eval-str with JavaScriptCore, a spike in memory will occur and it will take several seconds to complete the evaluation. After this, subsequent evaluations don't exhibit this behavior. Also, this doesn't occur for Firefox or Chrome.

Additionally, it did not occur previously with bootstrapped ClojureScript when we were building things outside of `cljs.js` (it doesn't occur in Replete, for example, nor the earlier versions of Planck: https://youtu.be/AMT63rxK4E8).

You can see it for yourself at http://ClojureScript.net (you can see the slowness in desktop Safari, and on an iPhone it exhausts memory).



 Comments   
Comment by Mike Fikes [ 30/Jul/15 10:58 AM ]

Attached memory.png, which shows what happens in Planck when you do (defn f [])

Comment by Mike Fikes [ 30/Jul/15 5:06 PM ]

This is the same as CLJS-910. An easy workaround is :static-fns true.





[CLJS-1376] Printing in a tagged literal data form Created: 28/Jul/15  Updated: 30/Jul/15  Resolved: 30/Jul/15

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 0.0-3308
Fix Version/s: None

Type: Task Priority: Major
Reporter: Ewen Grosjean Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File CLJS-1376.patch    

 Description   

Since Clojure 1.7, objects, functions, atoms and volatiles print in a tagged literal data form.

(atom {})
#object[clojure.lang.Atom 0x349c1daf {:status :ready, :val {}}]

Clojurescript format is different

(atom {})
#<Atom: {}>


 Comments   
Comment by Ewen Grosjean [ 30/Jul/15 3:52 PM ]

patch attached.

(fn [] nil)
#object ["function" "function (){\nreturn null;\n}"]
(js/AnimationEvent. 1)
#object ["object" "[object AnimationEvent]"]
(atom {})
#object [cljs.core.Atom {:status :ready, :val {}}]
(volatile! 1)
#object [cljs.core.Volatile 1]

I have also made undefined printing to nil.

I hope that helps.

Comment by David Nolen [ 30/Jul/15 4:01 PM ]

fixed https://github.com/clojure/clojurescript/commit/34c3b8985ed8197d90f441c46d168c4024a20eb8

Comment by David Nolen [ 30/Jul/15 4:03 PM ]

Ewen you need to submit a CA if you want to submit patches. Once you've done that you should assign tickets to yourself if you decide to work on them so people are aware of progress.

Comment by Ewen Grosjean [ 30/Jul/15 4:26 PM ]

Thanks for the fix.

I think there are still a few things missing.
Atom and volatile are still printed the old way: https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L9053
As is "undefined", even though undefined is printed as nil because it is catched by the nil? clause:
https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L8776





[CLJS-1378] JARs that end in .exe are problematic Created: 29/Jul/15  Updated: 30/Jul/15

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

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