jmmk / javascript-externs-generator

Generate externs for use with Google Closure Compiler
https://jmmk.github.io/javascript-externs-generator/
ISC License
135 stars 9 forks source link

Stack overflow on circular references #19

Closed weavejester closed 8 years ago

weavejester commented 8 years ago

The build-tree function has a (not (identical? obj child)) check in it, but it doesn't account for instances where a child links back to an earlier ancestor.

I've also noticed that some libraries (such as Pixi.js v4) occassionally hold references to DOM elements, which obviously we don't want to extern.

My hacky solution has been to update build-tree to hold a list of parents to check for circular references. This didn't seem to affect DOM elements, so I also added in an additional check for (.-nodeType child). You may well ask why I didn't use (instance? js/Node child)! Well, for some reason it didn't appear to work on the constructed canvas element, and I was unable to come up with a better check than to look for the existance of nodeType.

(defn build-tree
  ([obj name]
   (build-tree obj name ()))
  ([obj name parents]
   {:name      name
    :type      (get-type obj)
    :props     (for [k (js-keys obj)
                     :let [child (obj/get obj k)]]
                 (if (and (parent-type? child)
                          (not (or (identical? obj child)
                                   (.-nodeType child)
                                   (some (partial identical? child) parents))))
                   (build-tree child k (conj parents obj))
                   {:name k :type (get-type child)}))
    :prototype (for [k (js-keys (.-prototype obj))]
                 {:name k :type :function})}))

By the way, here's a link to the generated file I was using: http://www.booleanknot.com.s3.amazonaws.com/pixi.inc.js

If you try to extern "PIXI", the stack overflow error is generated.

jmmk commented 8 years ago

Thanks for reporting!

I have no access to a computer for a few days, but I will try to take a look when I am back. On Jun 13, 2016 5:14 PM, "James Reeves" notifications@github.com wrote:

The build-tree function has a (not (identical? obj child)) check in it, but it doesn't account for instances where a child links back to an earlier ancestor.

I've also noticed that some libraries (such as Pixi.js v4) occassionally hold references to DOM elements, which obviously we don't want to extern.

My hacky solution has been to update build-tree to hold a list of parents to check for circular references. This didn't seem to affect DOM elements, so I also added in an additional check for (.-nodeType child). You may well ask why I didn't use (instance? js/Node child)! Well, for some reason it didn't appear to work on the constructed canvas element, and I was unable to come up with a better check than to look for the existance of nodeType.

(defn build-tree ([obj name](build-tree obj name %28%29)) ([obj name parents] {:name name :type (get-type obj) :props (for [k (js-keys obj) :let [child (obj/get obj k)]](if %28and %28parent-type? child%29 %28not %28or %28identical? obj child%29 %28.-nodeType child%29 %28some %28partial identical? child%29 parents%29%29%29%29 %28build-tree child k %28conj parents obj%29%29 {:name k :type %28get-type child%29})) :prototype (for [k (js-keys (.-prototype obj))] {:name k :type :function})}))

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/jmmk/javascript-externs-generator/issues/19, or mute the thread https://github.com/notifications/unsubscribe/ABEfwjgns4oYextMJ5MtCsbP1rGeywfsks5qLcgdgaJpZM4I0vg6 .

jmmk commented 8 years ago

The reason the (instance? js/Node child) call does not work but nodeType does is because we are inside an iframe (see: http://stackoverflow.com/a/13895357). So child instanceof Node == false but child instanceof document.getElementById('sandbox').contentWindow.Node == true.

I am able to make these changes and see it working, but I'm unable to come up with a small test case for the circular reference.

weavejester commented 8 years ago

Maybe if you just create a DOM element and hold onto it? I believe DOM elements have a circular references around parents and siblings.

jmmk commented 8 years ago

Just the nodeType check handles the dom node reference: https://github.com/jmmk/javascript-externs-generator/commit/bc957ae39acd56f901e881e20ad191e6af44f028

weavejester commented 8 years ago

Hm, what about something like...

var x = {foo: {}};
x.foo.bar = x;

The problem was with circular references that were multiple levels deep, e.g. an element linking back to its grandparent.

jmmk commented 8 years ago

That did it, thanks!

weavejester commented 8 years ago

Thanks for fixing this!

jmmk commented 8 years ago

@weavejester thanks for the help! Just deployed the change to the web app