RESOLVED FIXED 137651
WeakMap reference w/ DOM element as key does not survive long enough
https://bugs.webkit.org/show_bug.cgi?id=137651
Summary WeakMap reference w/ DOM element as key does not survive long enough
Tim Guan-tin Chien [:timdream]
Reported 2014-10-12 19:35:28 PDT
STR: 1. Open the reduced test case, open up the Inspector 2. Click Run 3. Observer the console output Expected: 1. console keep printing the object from our setInterval call Actual: 1. After GC (?) the console start to porint out 'undefined' Note: Originally submitted as: https://github.com/timdream/gaia-keyboard-demo/issues/7 This is easier to reproduce on iOS 8 Simulator than on Safari 7.1, and harder on WebKit Nightly. Does not affect previous iOS or Safari versions because WeakMap was not implemented.
Attachments
Testcase (492 bytes, text/html)
2014-10-12 19:36 PDT, Tim Guan-tin Chien [:timdream]
no flags
The patch (6.91 KB, patch)
2015-05-29 11:52 PDT, Keith Miller
ggaren: review-
ggaren: commit-queue-
Patch 2 (296.44 KB, patch)
2015-05-29 13:32 PDT, Keith Miller
ggaren: review-
ggaren: commit-queue-
Patch Nomenclature (7.05 KB, patch)
2015-05-29 16:21 PDT, Keith Miller
ggaren: review-
ggaren: commit-queue-
Patch Added Expected (7.52 KB, patch)
2015-05-29 16:34 PDT, Keith Miller
no flags
Patch Expected File (7.52 KB, patch)
2015-05-29 16:35 PDT, Keith Miller
no flags
Tim Guan-tin Chien [:timdream]
Comment 1 2014-10-12 19:36:15 PDT
Created attachment 239710 [details] Testcase
Radar WebKit Bug Importer
Comment 2 2014-10-13 23:09:38 PDT
Geoffrey Garen
Comment 3 2014-10-14 11:53:04 PDT
In this test case, the JavaScript wrapper for the p element is garbage collected because there are no explicit references to it -- even though its underlying C++ DOM object is still present in the document. I guess the behavior you expected was for the JavaScript wrapper's lifetime to be identical to its underlying DOM object, as observed by a WeakMap. Ironically, implementing that feature would require a WeakMap to strongly reference a DOM object. I'm not sure what the correct behavior is in this case. Perhaps the optimization to throw away unobservable DOM wrappers is invalid in a world where WeakMap can observe the lifetime of any object.
Tim Guan-tin Chien [:timdream]
Comment 4 2014-10-14 19:18:34 PDT
Thanks for the comment, (In reply to comment #3) > In this test case, the JavaScript wrapper for the p element is garbage collected because there are no explicit references to it -- even though its underlying C++ DOM object is still present in the document. > Web Developers shouldn't be worry about how the DOM object is implemented right :) ? > I guess the behavior you expected was for the JavaScript wrapper's lifetime to be identical to its underlying DOM object, as observed by a WeakMap. > > Ironically, implementing that feature would require a WeakMap to strongly reference a DOM object. > > I'm not sure what the correct behavior is in this case. Perhaps the optimization to throw away unobservable DOM wrappers is invalid in a world where WeakMap can observe the lifetime of any object. FWIW Gecko and Chrome does not fail when I run this test case.
Alexey Proskuryakov
Comment 5 2014-10-14 22:16:44 PDT
I think that adding a custom property on each node in WeakMap may be a reasonable workaround. Geoff, does this have any serious downsides? while (i--) { document.body.appendChild(document.createElement('p')); document.body.lastElementChild.textContent = i; document.body.lastElementChild.webkitWeakMapWorkaround = 1; // <-- This wmap.set(document.body.lastElementChild, { foo: 'bar', id: i }); } Perhaps we can use a similar mechanism to make WeakMap work with nodes. Possibly a side HashCountedSet, or even a counter in node rare data.
Geoffrey Garen
Comment 6 2014-10-15 12:13:33 PDT
> I think that adding a custom property on each node in WeakMap may be a reasonable workaround. Geoff, does this have any serious downsides? > > while (i--) { > document.body.appendChild(document.createElement('p')); > document.body.lastElementChild.textContent = i; > document.body.lastElementChild.webkitWeakMapWorkaround = 1; // <-- This > wmap.set(document.body.lastElementChild, { foo: 'bar', id: i }); > } I think that's a reasonable workaround. > Perhaps we can use a similar mechanism to make WeakMap work with nodes. Possibly a side HashCountedSet, or even a counter in node rare data. I think we could. Ultimately, though, it might just be time to retire the "unobservable wrapper" optimization. Now that WeakMap and WeakSet expose GC semantics, everything is logically observable.
Alexey Proskuryakov
Comment 7 2014-10-15 12:44:08 PDT
> even a counter in node rare data Geoff pointed out that this won't work, because we likely have the same issue with some non-node objects (such as probably CSS OM). > Now that WeakMap and WeakSet expose GC semantics I don't think that they do - there is no way to iterate them, so you always need a reachable object for lookup.
Geoffrey Garen
Comment 8 2014-10-15 13:59:13 PDT
> > Now that WeakMap and WeakSet expose GC semantics > > I don't think that they do - there is no way to iterate them, so you always need a reachable object for lookup. What I mean by this is that the expose, in a programmer-visible way for the first-time, whether the engine decided to garbage collect a JavaScript wrapper that was not visible through any other API.
Joseph Pecoraro
Comment 9 2015-04-22 13:42:40 PDT
I just ran into this with WeakSet. It was unexpected to me to see a node disappear from a WeakSet. It is inconvenient and unexpected to have to work around this.
Keith Miller
Comment 10 2015-05-29 11:52:56 PDT
Created attachment 253904 [details] The patch
Filip Pizlo
Comment 11 2015-05-29 12:01:56 PDT
Comment on attachment 253904 [details] The patch View in context: https://bugs.webkit.org/attachment.cgi?id=253904&action=review > Source/WebCore/bindings/js/JSNodeCustom.cpp:79 > -static inline bool isObservable(JSNode* jsNode, Node* node) > -{ > - // The root node keeps the tree intact. > - if (!node->parentNode()) > - return true; > - > - if (jsNode->hasCustomProperties()) > - return true; > - > - // A node's JS wrapper is responsible for marking its JS event listeners. > - if (node->hasEventListeners()) > - return true; > - > - return false; > -} > - > -static inline bool isReachableFromDOM(JSNode* jsNode, Node* node, SlotVisitor& visitor) > +static inline bool isReachableFromDOM(Node* node, SlotVisitor& visitor) Hmmm, it seems like we should do some performance tests to make sure that this isn't too severe.
Geoffrey Garen
Comment 12 2015-05-29 12:16:33 PDT
Comment on attachment 253904 [details] The patch Please add a comment in your ChangeLog to explain your performance testing methodology.
Keith Miller
Comment 13 2015-05-29 13:32:26 PDT
Created attachment 253909 [details] Patch 2 Updated the ChangeLog to reflect the performance testing strategy for this patch.
Geoffrey Garen
Comment 14 2015-05-29 14:39:06 PDT
Comment on attachment 253909 [details] Patch 2 View in context: https://bugs.webkit.org/attachment.cgi?id=253909&action=review Looks like you have some accidental whitespace changes throughout the ChangeLog. Did you reflow the document? > Source/WebCore/ChangeLog:11 > + will be done after the code is rolled out. If major > + performance issues occur the patch will be rolled back. We use "rolled out" to mean removing the patch. So, you are going to test by monitor bots after committing the change, and then you will roll out if there is a problem.
Keith Miller
Comment 15 2015-05-29 16:21:39 PDT
Created attachment 253929 [details] Patch Nomenclature Undid the whitespace problem and changed the wording to be in line with the rest of the team.
Geoffrey Garen
Comment 16 2015-05-29 16:23:57 PDT
Comment on attachment 253929 [details] Patch Nomenclature Oops! Didn't notice this before: Regression test needs expected results.
Keith Miller
Comment 17 2015-05-29 16:34:20 PDT
Created attachment 253933 [details] Patch Added Expected Added the expected test results file.
Keith Miller
Comment 18 2015-05-29 16:35:37 PDT
Created attachment 253934 [details] Patch Expected File
Geoffrey Garen
Comment 19 2015-05-29 16:36:58 PDT
Comment on attachment 253934 [details] Patch Expected File r=me
WebKit Commit Bot
Comment 20 2015-05-29 17:27:03 PDT
Comment on attachment 253934 [details] Patch Expected File Clearing flags on attachment: 253934 Committed r185023: <http://trac.webkit.org/changeset/185023>
WebKit Commit Bot
Comment 21 2015-05-29 17:27:07 PDT
All reviewed patches have been landed. Closing bug.
Alexey Proskuryakov
Comment 22 2015-05-29 19:46:39 PDT
This broke bindings tests, which need to be updated. Is anyone around to do that now? I can only do it later in the evening.
Joseph Pecoraro
Comment 23 2015-05-29 20:52:23 PDT
Note You need to log in before you can comment on or make changes to this bug.