<< Back to previous view

[CLJS-439] IEncodeClojure only works on same-context Objects Created: 11/Dec/12  Updated: 21/Sep/16  Resolved: 20/Jan/13

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

Type: Defect Priority: Minor
Reporter: Tom Jack Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: None


The default impl uses (identical? (type x) js/Object), but objects created in another JS context (e.g. another frame) will fail this test, since their constructor is a different js/Object. Thus js->clj is the identity on such objects.

I wonder if there are any related problems anywhere else, e.g. with protocols? This seems to be the only occurrence of js/Object.

Maybe this can be fixed by extending IEncodeClojure to object? I don't immediately see how to do that without incurring the option destructuring overhead recursively.

Comment by David Nolen [ 13/Dec/12 7:55 AM ]

I don't think this is something that ClojureScript should try to address at all. To me it seems similar to various classloader issues in JVM land.

Comment by Tom Jack [ 15/Dec/12 2:31 AM ]

OK, I don't disagree.

Comment by Sean Grove [ 17/Dec/12 12:33 AM ]

This is causing some unpleasantness, and I'm not aware of the classloader issues. Why check (identical? js/object x) instead of (goog.isObject x) on https://github.com/clojure/clojurescript/blob/master/src/cljs/cljs/core.cljs#L6948 ?

Comment by Chris Granger [ 17/Dec/12 1:26 AM ]

this bit me as well and I can't see a downside to using goog.isObject. I agree with David about not going down a rabbit hole here, but I think we can do the "right thing" for free.

EDIT: I spoke too soon. Looks like native objects, like dom nodes would blow up in this scenario. Also, goog.isObject returns true on functions, but that would be easy enough to deal with.

Comment by Tom Jack [ 17/Dec/12 2:31 AM ]

I suppose extending to object would similarly cause trouble for dom nodes etc?

If you can get ahold of the js/Object from another frame, is it possible to extend IEncodeClojure to it?

Comment by David Nolen [ 21/Dec/12 5:50 PM ]

I have some experience with this stuff - it's a rabbit hole. There are many objects that can cross contexts for which we can provide no sensible equality guarantees. If you need to move data between JS contexts then use GClosure and stick to the basic JS data types. I'm inclined to close this one unless someone has a brilliant comprehensive solution that I'm not seeing right now.

Comment by David Nolen [ 20/Jan/13 12:52 AM ]

This one is tricky. Closing for now.

Comment by Richard Newman [ 21/Sep/16 12:03 PM ]

I just ran into this.

js->clj works perfectly in a Node environment.

It doesn't work in a Firefox add-on, where an object is created in the add-on code and processed by cljs code loaded through `require`.

This cost me quite a bit of debugging time, because of course it works fine when you test it inside the same source file, and works fine in Node, which is a much simpler JS environment.

JS engine documentation makes it very clear that you should not do comparisons against prototypes, because it's nonsensical:


The naïve workaround with the current implementation — to convert a JS object to a cljs object in the calling context – is difficult, because of course the calling context is pure JS (so it doesn't even have a js->clj function!), and if it did, it would be a different cljs, and the same problem would occur elsewhere when handling that cljs object.

Using `(js->clj (.parse js/JSON (.stringify js/JSON o)))` is an expensive alternative, and doesn't work for rich objects (including JS's own Date type).

So much as it's tempting to say "too hard, wontfix", this makes building a non-trivial library in ClojureScript very inconvenient indeed.

Proposed solutions:

  • Provide a js->clj function that assumes that the input is a JS type, and converts it through sane JS-appropriate logic. E.g., it calls Array.isArray(o) to determine whether the input is an array. Object is the final fallback.
  • Adjust the existing js->clj function that inverts its test: checking whether the input is a ClojureScript type first (because of course they're all objects), and otherwise falling through correct JS type determination code.

For the moment I am going to write the former myself, because I don't have any more time to waste.

Comment by Richard Newman [ 21/Sep/16 3:59 PM ]

Here's an implementation that works for my purposes. Feel free to extend or incorporate this into ClojureScript under the EPL, MPL-2.0, or otherwise.


Generated at Wed Oct 26 14:36:44 CDT 2016 using JIRA 4.4#649-r158309.