<< Back to previous view

[CLJS-891] Defs in "parent" namespaces clash with "child" namespaces with the same name? Created: 28/Nov/14  Updated: 02/Dec/14

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

Type: Defect Priority: Major
Reporter: Russell Dunphy Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug, namespace
Environment:

Clojurescript 0.0-2371, OSX



 Description   

This has had me totally flummoxed for a few hours, but I have finally been able to create a minimal project replicating the problem based on the mies template: https://github.com/rsslldnphy/cljs-ns-issue.

The problem seems to happen when a "parent" namespace, for example `my-project.foo`, contains a def with the same name as a "child" namespace, eg if there is a namespace `my-project.foo.bar`, and a def named `bar` in `my-project.foo`, and both those namespaces are required by `my-project.core`, then calling functions in `my-project.foo.bar` ends up with an Uncaught TypeError: Cannot read property 'call' of undefined. Sometimes, depending on which ns requires which, I've also seen an Uncaught Error: Namespace "cljs_ns_issue.foo.bar" already declared.

I don't think I'm doing a particularly good job of explaining this so it might be easier to look at the code. The crux is: comment out this line and the code works, leave it in and you get an error.



 Comments   
Comment by Francis Avila [ 28/Nov/14 2:01 PM ]

Clojurescript implements namespaces with Google Closure compiler's require/provide system. Unfortunately that system does not have a hard distinction between names and namespaces like Clojure does but instead is more like a sloppy java classname. The crux of it is that vars and namespaces occupy the same tree of js objects and thus their names may not overlap.

Compare the cljs on the left with the emitted javascript (This isn't exactly what is happening, but only in essence):

(ns my-project.foo ; goog.provide('my_project.foo') // something like window.my_project = {foo: {}}
  (:require my-project.foo.bar)) //goog.require('my_project.foo.bar')

;; the "require" evaluates the other namespace, which sets
;; // window.my_project.foo = {bar:{}};
;; // my_project.foo.bar.baz = "some var in the bar ns";
;; Now window.my_project.foo.bar = {baz: "some var in the bar ns"};

(defn bar [] 1)         ; my_project.foo.bar = (function bar(){return 1;});
;; Now the js object that was the bar namespace is gone, replaced with this function.

(my-project.foo.bar/baz)
; my_project.foo.bar.baz.call() // Uncaught TypeError: Cannot read property 'call' of undefined.

;; Alternatively, if (ns my-project.foo.bar) got evaluated *after* my-project.foo namespace was
;; evaluated, then my-project.foo/bar is defined, and the emitted goog.provide('my-project.foo.bar')
;; throws "Uncaught Error: Namespace "my_project.foo.bar" already declared".

So basically this is a leaky abstraction. In Clojurescript, you cannot define a var whose ns-qualified name matches that of another namespace: the slash between name and namespace is not real.

I think the only possible things that can be done are either:

  • Warn at compile-time if a var and a namespace object-path clash. Obviously there may still be runtime-created vars.
  • Put namespace vars behind some magic name. E.g. (ns foo.bar)(def baz 1) becomes goog.provide('foo.bar.__NS__'); foo.bar.__NS__.baz = 1; This would significantly uglify cljs names exported to js (and break existing code), and the magic name could never be used as a var name.
Comment by David Nolen [ 02/Dec/14 6:07 AM ]

We actually already have some logic for this in place, we track namespace segments for this reason. This is a different manifestation of it than previously encountered. I think any further workarounds are likely more trouble than they are worth (debugging complications) - probably the best thing to do is report a warning if we detect a clash.





[CLJS-799] Having a namespace end with ".cljs" produces wrong source map Created: 16/Apr/14  Updated: 02/Dec/14

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

Type: Defect Priority: Minor
Reporter: Sven Richter Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: maps, namespace, source
Environment:

Windows 7
JDK 1.7
CLJS version: 0.0-2138 and 0.0-2156 (probably hits other versions too, but I only tested these two)



 Description   

When an clojurescript namespaces ends with ".cljs" I cannot see the source file in google chrome.
Repro steps:

1. Create a new luminus project with: lein new luminus cljsbug +cljs +http-kit
2. Change the project.clj cljsbuild -> compiler setting to:

:compiler
{:output-to "resources/public/js/site.js"
:output-dir "resources/public/js/out"
:optimizations :none
:source-map true
}

3. Change cljsexample.html file to:

<script type="text/javascript" src="js/out/goog/base.js"></script>
<script type="text/javascript" src="{{servlet-context}}/js/site.js"></script>
<script type="text/javascript">goog.require("cljsbug.main");</script>

4. Now start the server with "lein run -dev" and "lein cljsbuild auto"
5. Open localhost:3000/cljsexample
6. Check for the source file in google chrome

It should be there now and correct.
Now to reproduce the problem do this:

7. Change the namespace of the main.cljs file to: ns cljsbug.main.cljs
8. Change the cljsexample.html goog.require line to:

<script type="text/javascript">goog.require("cljsbug.main.cljs");</script>

9. Restart the cljsbuild with: lein do cljsbuild clean, cljsbuild auto
10. Reload the /cljsexample page in google chrome and the source mapping wont be there anymore.



 Comments   
Comment by Sven Richter [ 16/Apr/14 2:38 PM ]

Just to clear things up. Steps 1 to 6 are not needed to reproduce the problem. It is sufficient to go through steps 7 to 10 on any project that uses a similar cljsbuild setting.
It is important that optimizations are set to :none.

Short repro version.

1. Have cljs project with the following settings:
:compiler
{:output-to "resources/public/js/site.js"
:output-dir "resources/public/js/out"
:optimizations :none
:source-map true
}

2. Change any cljs file namespace and add ".cljs" to the namespace.
3. Have the new namespace required in the html file by google like this: goog.require("cljsbug.main.cljs")

4. Open a page in the browser and see that the source maps are missing.

Comment by David Nolen [ 02/Dec/14 5:41 AM ]

A patch for this is welcome!





[CLJS-697] top-level symbol reference doesn't get an automatically inserted ns-name Created: 23/Nov/13  Updated: 23/Nov/13  Resolved: 23/Nov/13

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

Type: Defect Priority: Major
Reporter: Limbo Peng Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: Compiler, bug, namespace
Environment:

org.clojure/clojurescript "0.0-2030"



 Description   

I'm trying to use a Node.js module (with nested namespaces) in ClojureScript - the code goes like this:

(ns myapp)
(def evernote (js/require "evernote"))
(def token "TOKEN")
(defn do-sth []
  (let [client (evernote.Evernote.Client. (js-obj "token" token))]
    (.log js/console client)))
(do-sth)

which gets compiled (with :simple optimization) to:

var myapp = {}
myapp.evernote = require("evernote")
myapp.token = "TOKEN"
myapp.do_sth = function() {
  var a = new evernote.Evernote.Client({token:myapp.token})
  return console.log(a)
}
myapp.do_sth()

which will obviously fail with error "Uncaught ReferenceError: evernote is not defined".



 Comments   
Comment by David Nolen [ 23/Nov/13 11:55 PM ]

fixed, https://github.com/clojure/clojurescript/commit/d4bf88269e1d96468a19fd481f32628d4eafec9d





[CLJS-574] No warnings when calling functions from unrequired namespaces Created: 24/Aug/13  Updated: 05/Oct/13  Resolved: 05/Oct/13

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

Type: Defect Priority: Major
Reporter: Curtis Gagliardi Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: namespace
Environment:

Clojure 1.5.1, Cojurescript "0.0-1859", cljs-bulid 0.3.2



 Description   

This will compile without warnings:
(ns example.nspace)
(non-existent-ns/func "example")

There's also no warning for undeclared functions in an existing namespace:

(ns example.nspace)
(existing-namespace/non-existent-function "example")

will also compile without warnings.

Pushed the code I was using to make sure I wasn't crazy here: https://github.com/cgag/clojurescript-issue



 Comments   
Comment by David Nolen [ 29/Sep/13 2:44 PM ]

Sorry closed wrong ticket.

Comment by David Nolen [ 05/Oct/13 2:38 PM ]

fixed, http://github.com/clojure/clojurescript/commit/4769c5e05233d9841aedf882bd99812d0f842abb





[CLJS-282] When compiling incrementally, goog dependencies can get out of order (breaks builds) Created: 29/May/12  Updated: 27/Jul/13  Resolved: 19/Nov/12

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

Type: Defect Priority: Major
Reporter: Evan Mezeske Assignee: David Nolen
Resolution: Completed Votes: 4
Labels: bug,, namespace

Attachments: Text File cljs_282.patch     Text File dependencies-out-of-order.txt    

 Description   

In lein-cljsbuild version 0.2.0, I changed it to allow incremental compilation again. Previous versions would always clean out the :output-dir before each call to closure/build, because I had seen weird issues before but never tracked them down.

Finally, I have pinpointed the problem with incremental compilation (although not the solution). The problem is that sometimes, when the compiler notices that a ClojureScript file has already been compiled to JavaScript, its JavaScript output will be inserted into the :whitespace (or higher) optimized output in the wrong order. By "wrong order", I mean its goog.provide() comes after a goog.require() for that same namespace.

I've attached a snippet from my :whitespace compiled output for a closed source project. I removed a bunch of code from the attachment, but I did not change the order of anything. As you can see, ss_charts.crossover.url is provided after it is required.

My guess is that the problem originates in compiler/compile-file. It calls requires-compilation?, and returns a hash containing only {:file ...} if it does not require compilation. I assume that the problem has to do with the fact that this hash lacks the :provides and :requires keys, and thus down the road doesn't get considered in the dependency graph correctly.



 Comments   
Comment by David Nolen [ 29/May/12 11:50 AM ]

Seems like a reasonable guess. Patch welcome.

Comment by Arlen Christian Mart Cuss [ 05/Nov/12 11:29 PM ]

There's a minimalish reproducible example of this here: https://github.com/unnali/cljs-sscce.

Comment by David Nolen [ 18/Nov/12 6:41 PM ]

I'm unable to recreate the issue myself when using the example project. I've created a patch that may fix the issue for others. Can someone please confirm? You can test the patch by making a checkouts directory, clone ClojureScript into it, apply the patch there and then add the following to your project.clj:

:extra-classpath-dirs ["checkouts/clojurescript/src/clj"
                       "checkouts/clojurescript/src/cljs"]
Comment by Arlen Christian Mart Cuss [ 18/Nov/12 7:14 PM ]

Unfortunately, this didn't fix the issue for me. I've just realised the cljs-sscce as linked had ":incremental false" set in project.clj, which stops the issue from being reproduced. If you didn't notice that (I didn't until I came to try and found it "working" pre-patch!), it might be worth trying again with that.

If you still can't reproduce it, let me know—I can try preparing a VM image or similar?

Comment by David Nolen [ 18/Nov/12 7:36 PM ]

OK, I was able to reproduce the original issue. I can also confirm that the issue is fixed for me per the instructions on the cljs-sscce repo. I note that it's not really possible to confirm w/ my instructions in the comment above with Lein 2.

Comment by Arlen Christian Mart Cuss [ 18/Nov/12 9:10 PM ]

My testing's with Lein 2, so as David noted the fact that it didn't work for me is no indication that the patch doesn't work!

Comment by David Nolen [ 19/Nov/12 9:44 AM ]

I believe you may be able to confirm the patch by skipping the checkouts bit, just clone clojurescript to some directory in your project and set :extra-classpath-dirs.

Comment by David Nolen [ 19/Nov/12 12:00 PM ]

I checked with Phil Hagelberg, in Lein 2 instead of using :extra-classpath-dirs you need to use :resource-paths and no need to create the checkouts directory.

Comment by David Nolen [ 19/Nov/12 1:37 PM ]

fixed, http://github.com/clojure/clojurescript/commit/a27f811f046cd1ace9e4b9461181e5a5fb23b682





[CLJS-121] `binding` doesn't work with vars from :require'd namespaces Created: 10/Jan/12  Updated: 27/Jul/13  Resolved: 10/Jan/12

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

Type: Defect Priority: Major
Reporter: Shantanu Kumar Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: namespace
Environment:

clojurescript-0.0.1-329708bdd0.jar
closure-compiler-r1592.jar
closure-library-r1376.jar



 Description   

Compiling main.js from src-cljs...Exception in thread "main" java.lang.RuntimeException: java.lang.AssertionError: Assert failed: Invalid local name: vars/*slot-text*2884
(not (or (namespace name) (.contains (str name) ".")))
at clojure.lang.Util.runtimeException(Util.java:165)
at clojure.lang.Compiler.eval(Compiler.java:6476)
at clojure.lang.Compiler.eval(Compiler.java:6455)
at clojure.lang.Compiler.eval(Compiler.java:6431)
at clojure.core$eval.invoke(core.clj:2795)
at clojure.main$eval_opt.invoke(main.clj:296)
at clojure.main$initialize.invoke(main.clj:315)
at clojure.main$null_opt.invoke(main.clj:348)
at clojure.main$main.doInvoke(main.clj:426)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:405)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:518)
at clojure.main.main(main.java:37)
Caused by: java.lang.AssertionError: Assert failed: Invalid local name: vars/*slot-text*2884
(not (or (namespace name) (.contains (str name) ".")))
at cljs.compiler$analyze_let$fn__1110.invoke(compiler.clj:730)
at cljs.compiler$analyze_let.invoke(compiler.clj:724)
at cljs.compiler$eval1119$fn__1120.invoke(compiler.clj:746)
at clojure.lang.MultiFn.invoke(MultiFn.java:177)
at cljs.compiler$analyze_seq.invoke(compiler.clj:958)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze_seq.invoke(compiler.clj:960)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze_seq.invoke(compiler.clj:960)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze.invoke(compiler.clj:1004)
at cljs.compiler$analyze_block.invoke(compiler.clj:595)
at cljs.compiler$analyze_fn_method$fn__1086.invoke(compiler.clj:691)
at cljs.compiler$analyze_fn_method.invoke(compiler.clj:690)
at cljs.compiler$eval1091$fn_1093$fn_1096.invoke(compiler.clj:706)
at clojure.core$map$fn__3811.invoke(core.clj:2432)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:60)
at clojure.lang.RT.seq(RT.java:466)
at clojure.core$seq.invoke(core.clj:133)
at clojure.core$map$fn__3811.invoke(core.clj:2424)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:60)
at clojure.lang.RT.seq(RT.java:466)
at clojure.core$seq.invoke(core.clj:133)
at clojure.core$apply.invoke(core.clj:600)
at cljs.compiler$eval1091$fn__1093.invoke(compiler.clj:707)
at clojure.lang.MultiFn.invoke(MultiFn.java:177)
at cljs.compiler$analyze_seq.invoke(compiler.clj:958)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze_seq.invoke(compiler.clj:960)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze.invoke(compiler.clj:1004)
at cljs.compiler$analyze_block.invoke(compiler.clj:595)
at cljs.compiler$analyze_let$fn__1115.invoke(compiler.clj:740)
at cljs.compiler$analyze_let.invoke(compiler.clj:739)
at cljs.compiler$eval1119$fn__1120.invoke(compiler.clj:746)
at clojure.lang.MultiFn.invoke(MultiFn.java:177)
at cljs.compiler$analyze_seq.invoke(compiler.clj:958)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze_seq.invoke(compiler.clj:960)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze.invoke(compiler.clj:1004)
at cljs.compiler$analyze_let$fn__1110.invoke(compiler.clj:731)
at cljs.compiler$analyze_let.invoke(compiler.clj:724)
at cljs.compiler$eval1119$fn__1120.invoke(compiler.clj:746)
at clojure.lang.MultiFn.invoke(MultiFn.java:177)
at cljs.compiler$analyze_seq.invoke(compiler.clj:958)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze_seq.invoke(compiler.clj:960)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze.invoke(compiler.clj:1004)
at cljs.compiler$analyze_block.invoke(compiler.clj:596)
at cljs.compiler$analyze_fn_method$fn__1086.invoke(compiler.clj:691)
at cljs.compiler$analyze_fn_method.invoke(compiler.clj:690)
at cljs.compiler$eval1091$fn_1093$fn_1096.invoke(compiler.clj:706)
at clojure.core$map$fn__3811.invoke(core.clj:2432)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:60)
at clojure.lang.RT.seq(RT.java:466)
at clojure.core$seq.invoke(core.clj:133)
at clojure.core$map$fn__3811.invoke(core.clj:2424)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:60)
at clojure.lang.RT.seq(RT.java:466)
at clojure.core$seq.invoke(core.clj:133)
at clojure.core$apply.invoke(core.clj:600)
at cljs.compiler$eval1091$fn__1093.invoke(compiler.clj:707)
at clojure.lang.MultiFn.invoke(MultiFn.java:177)
at cljs.compiler$analyze_seq.invoke(compiler.clj:958)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze_seq.invoke(compiler.clj:960)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$eval1068$fn_1069$fn_1072.invoke(compiler.clj:662)
at cljs.compiler$eval1068$fn__1069.invoke(compiler.clj:661)
at clojure.lang.MultiFn.invoke(MultiFn.java:177)
at cljs.compiler$analyze_seq.invoke(compiler.clj:958)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze_seq.invoke(compiler.clj:960)
at cljs.compiler$analyze.invoke(compiler.clj:1011)
at cljs.compiler$analyze.invoke(compiler.clj:1004)
at cljs.compiler$compile_file_STAR_.invoke(compiler.clj:1071)
at cljs.compiler$compile_file.invoke(compiler.clj:1109)
at cljs.compiler$compile_root.invoke(compiler.clj:1169)
at cljs.closure$compile_dir.invoke(closure.clj:304)
at cljs.closure$eval1653$fn__1654.invoke(closure.clj:336)
at cljs.closure$eval1584$fn_1585$G1575_1592.invoke(closure.clj:215)
at cljs.closure$eval1640$fn__1641.invoke(closure.clj:350)
at cljs.closure$eval1584$fn_1585$G1575_1592.invoke(closure.clj:215)
at cljs.closure$build.invoke(closure.clj:785)
at cljsbuild.core$compile_cljs.invoke(core.clj:37)
at cljsbuild.core$run_compiler.invoke(core.clj:123)
at user$eval1925.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6465)
... 12 more



 Comments   
Comment by Shantanu Kumar [ 10/Jan/12 8:04 AM ]

I accidentally pressed Submit before I could fill in all details. This exception occurs when I execute some code rebinding a var from a :require'd namespace. The scenario is below:

(ns foo
(:require [foo.vars :as vars]))

(binding [vars/*slot-text* :some-value]
...)

Comment by David Nolen [ 10/Jan/12 11:04 PM ]

Fixed, https://github.com/clojure/clojurescript/commit/37c8af6d1e35156c0453642ef84ec891bdd0325f





Generated at Thu Dec 18 04:35:37 CST 2014 using JIRA 4.4#649-r158309.