Bug 45426 - [Chromium] fast/dom/dataset-gc.html is failing
Summary: [Chromium] fast/dom/dataset-gc.html is failing
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore JavaScript (show other bugs)
Version: 528+ (Nightly build)
Hardware: PC OS X 10.5
: P2 Normal
Assignee: Erik Arvidsson
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-09-08 17:33 PDT by Dirk Pranke
Modified: 2010-09-21 09:34 PDT (History)
7 users (show)

See Also:


Attachments
Work in progress (4.20 KB, patch)
2010-09-13 18:10 PDT, Erik Arvidsson
no flags Details | Formatted Diff | Diff
Patch (5.41 KB, patch)
2010-09-14 18:45 PDT, Erik Arvidsson
no flags Details | Formatted Diff | Diff
Now uses V8DOMWrapper::getWrapper which handles the different kind of elements we might have (4.98 KB, patch)
2010-09-17 14:15 PDT, Erik Arvidsson
no flags Details | Formatted Diff | Diff
Changed to use setHiddenWindowReference which is more inline with what the JSC bindings do (4.98 KB, patch)
2010-09-17 15:08 PDT, Erik Arvidsson
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Dirk Pranke 2010-09-08 17:33:01 PDT
This test was introduced in r66954 (see bug 45358). If I understand the conversation on #webkit correctly, the wrapper object is being recreated incorrectly instead of persisting and being tied to the lifetime of the element.

JamesR suggests that we need to add a call to V8DomWrapper::setHIddenReference() at around line 860 of CodeGeneratorV8.pm ... patch to follow shortly.
Comment 1 Erik Arvidsson 2010-09-13 18:10:23 PDT
Created attachment 67498 [details]
Work in progress
Comment 2 Erik Arvidsson 2010-09-13 18:12:25 PDT
The test still fails after my changes but I don't understand why. This seems to follow the same pattern as NamedNodeMap.

Nate, do you have any ideas?
Comment 3 Nate Chapin 2010-09-14 13:20:29 PDT
(In reply to comment #2)
> The test still fails after my changes but I don't understand why. This seems to follow the same pattern as NamedNodeMap.
> 
> Nate, do you have any ideas?

Are we sure NamedNodeMap is right?  If I'm reading this correctly, your code is tying the Element wrapper's lifetime to that of the DOMStringMap, when we want the opposite.

:(
Comment 4 Erik Arvidsson 2010-09-14 18:45:42 PDT
Created attachment 67631 [details]
Patch
Comment 5 Nate Chapin 2010-09-15 10:42:50 PDT
Comment on attachment 67631 [details]
Patch


> @@ -44,6 +44,7 @@
>  #include "V8DedicatedWorkerContext.h"
>  #include "V8DOMApplicationCache.h"
>  #include "V8DOMMap.h"
> +#include "V8DOMStringMap.h"
>  #include "V8DOMWindow.h"
>  #include "V8EventListenerList.h"
>  #include "V8EventSource.h"

Remove include

> +v8::Handle<v8::Value> toV8(DOMStringMap* impl)
> +{
> +    if (!impl)
> +        return v8::Null();
> +    v8::Handle<v8::Object> wrapper = V8DOMStringMap::wrap(impl);
> +    // Add a hidden reference from the element to the DOMStringMap.
> +    Element* element = impl->element();
> +    if (!wrapper.IsEmpty() && element)
> +        V8DOMWrapper::setHiddenReference(V8Element::wrap(element), wrapper);

I think this needs to be toV8(element) rather than V8Element::wrap(element). wrap() may not handle subclasses of Element correctly.
Comment 6 Erik Arvidsson 2010-09-15 11:50:09 PDT
(In reply to comment #5)
> (From update of attachment 67631 [details])
> 
> > @@ -44,6 +44,7 @@
> >  #include "V8DedicatedWorkerContext.h"
> >  #include "V8DOMApplicationCache.h"
> >  #include "V8DOMMap.h"
> > +#include "V8DOMStringMap.h"
> >  #include "V8DOMWindow.h"
> >  #include "V8EventListenerList.h"
> >  #include "V8EventSource.h"
> 
> Remove include

Done.

> > +v8::Handle<v8::Value> toV8(DOMStringMap* impl)
> > +{
> > +    if (!impl)
> > +        return v8::Null();
> > +    v8::Handle<v8::Object> wrapper = V8DOMStringMap::wrap(impl);
> > +    // Add a hidden reference from the element to the DOMStringMap.
> > +    Element* element = impl->element();
> > +    if (!wrapper.IsEmpty() && element)
> > +        V8DOMWrapper::setHiddenReference(V8Element::wrap(element), wrapper);
> 
> I think this needs to be toV8(element) rather than V8Element::wrap(element). wrap() may not handle subclasses of Element correctly.

toV8 returns a V8::Value* but we need a V8::Object*
Comment 7 Nate Chapin 2010-09-15 11:53:30 PDT
(In reply to comment #6)
> (In reply to comment #5)
> > (From update of attachment 67631 [details] [details])
> > 
> > > @@ -44,6 +44,7 @@
> > >  #include "V8DedicatedWorkerContext.h"
> > >  #include "V8DOMApplicationCache.h"
> > >  #include "V8DOMMap.h"
> > > +#include "V8DOMStringMap.h"
> > >  #include "V8DOMWindow.h"
> > >  #include "V8EventListenerList.h"
> > >  #include "V8EventSource.h"
> > 
> > Remove include
> 
> Done.
> 
> > > +v8::Handle<v8::Value> toV8(DOMStringMap* impl)
> > > +{
> > > +    if (!impl)
> > > +        return v8::Null();
> > > +    v8::Handle<v8::Object> wrapper = V8DOMStringMap::wrap(impl);
> > > +    // Add a hidden reference from the element to the DOMStringMap.
> > > +    Element* element = impl->element();
> > > +    if (!wrapper.IsEmpty() && element)
> > > +        V8DOMWrapper::setHiddenReference(V8Element::wrap(element), wrapper);
> > 
> > I think this needs to be toV8(element) rather than V8Element::wrap(element). wrap() may not handle subclasses of Element correctly.
> 
> toV8 returns a V8::Value* but we need a V8::Object*

Nevermind.  The code generator changed and I missed it.  I was think of what is now wrapSlow()
Comment 8 Erik Arvidsson 2010-09-15 14:38:29 PDT
Is this good to go?

I'm a bit concerned about using V8Element::wrap since toV8(element) checks isHTMLElement and isSVGElement and calls toV8 on those. I guess I could do the same thing but I'm not sure it is necessary?
Comment 9 Nate Chapin 2010-09-15 14:43:31 PDT
(In reply to comment #8)
> Is this good to go?
> 
> I'm a bit concerned about using V8Element::wrap since toV8(element) checks isHTMLElement and isSVGElement and calls toV8 on those. I guess I could do the same thing but I'm not sure it is necessary?

So my knowledge of these functions (and which one is which) is apparently out of date, but yes, I'm pretty sure you need to be doing the isHTMLElement and isSVGElement calls.  We generally need to wrap an object as its real type to avoid problems when unwrapping them.
Comment 10 Erik Arvidsson 2010-09-17 14:15:20 PDT
Created attachment 67948 [details]
Now uses V8DOMWrapper::getWrapper which handles the different kind of elements we might have
Comment 11 Erik Arvidsson 2010-09-17 15:08:54 PDT
Created attachment 67958 [details]
Changed to use setHiddenWindowReference which is more inline with what the JSC bindings do
Comment 12 Nate Chapin 2010-09-17 15:32:56 PDT
Comment on attachment 67958 [details]
Changed to use setHiddenWindowReference which is more inline with what the JSC bindings do


> +v8::Handle<v8::Value> toV8(DOMStringMap* impl)
> +{
> +    if (!impl)
> +        return v8::Null();
> +    v8::Handle<v8::Object> wrapper = V8DOMStringMap::wrap(impl);
> +    // Add a hidden reference from the element to the DOMStringMap.
> +    Element* element = impl->element();
> +    if (!wrapper.IsEmpty() && element)
> +        V8DOMWrapper::setHiddenWindowReference(element->document()->frame(), wrapper);
> +    return wrapper;

If that's what JSC does, I guess it's ok to do the same. I don't know anything about DOMStringMap, so I don't have a good idea whether it make sense to keep it around for the life of the global object.  It seems like the default answer should be to tie it to its owner object, though.

Where do the JSC bindings do this?  I didn't immediately see it.
Comment 13 Erik Arvidsson 2010-09-17 15:41:12 PDT
(In reply to comment #12)
> Where do the JSC bindings do this?  I didn't immediately see it.

It is done in WebCore/bindings/js/JSElementCustom.cpp

 63     markDOMObjectWrapper(markStack, globalData, element->optionalDataset());

This was done in Bug 45358
Comment 14 Adam Barth 2010-09-18 18:05:26 PDT
Comment on attachment 67958 [details]
Changed to use setHiddenWindowReference which is more inline with what the JSC bindings do

Clearing flags on attachment: 67958

Committed r67805: <http://trac.webkit.org/changeset/67805>
Comment 15 Adam Barth 2010-09-18 18:05:32 PDT
All reviewed patches have been landed.  Closing bug.
Comment 16 anton muhin 2010-09-20 04:32:23 PDT
(In reply to comment #11)
> Created an attachment (id=67958) [details]
> Changed to use setHiddenWindowReference which is more inline with what the JSC bindings do

Erik, apparently your patch doesn't do what JSC does: JSC makes DOMStringMap to live as long as elements is live, and you make it live as long as global object is alive, so it is apparently a leak.

There is another approach you may use which would be more inline with the way object lives are treated in v8 bindings: you could make a group out of element's and DOMStringMap's---in this case they would survive or get GCed together which is apparently what you're looking for.
Comment 17 Erik Arvidsson 2010-09-20 10:32:38 PDT
(In reply to comment #16)
> (In reply to comment #11)
> > Created an attachment (id=67958) [details] [details]
> > Changed to use setHiddenWindowReference which is more inline with what the JSC bindings do
> 
> Erik, apparently your patch doesn't do what JSC does: JSC makes DOMStringMap to live as long as elements is live, and you make it live as long as global object is alive, so it is apparently a leak.

That is how I understood the JSC code. I should have known better though.

> There is another approach you may use which would be more inline with the way object lives are treated in v8 bindings: you could make a group out of element's and DOMStringMap's---in this case they would survive or get GCed together which is apparently what you're looking for.

Yes, do you have any pointers?
Comment 18 anton muhin 2010-09-20 10:39:34 PDT
(In reply to comment #17)
> (In reply to comment #16)
> > (In reply to comment #11)
> > > Created an attachment (id=67958) [details] [details] [details]
> > > Changed to use setHiddenWindowReference which is more inline with what the JSC bindings do
> > 
> > Erik, apparently your patch doesn't do what JSC does: JSC makes DOMStringMap to live as long as elements is live, and you make it live as long as global object is alive, so it is apparently a leak.
> 
> That is how I understood the JSC code. I should have known better though.
> 
> > There is another approach you may use which would be more inline with the way object lives are treated in v8 bindings: you could make a group out of element's and DOMStringMap's---in this case they would survive or get GCed together which is apparently what you're looking for.
> 
> Yes, do you have any pointers?

Sure, take a look at http://www.google.com/codesearch/p?hl=en#N6Qhr5kJSgQ/WebCore/bindings/v8/V8GCController.cpp&q=file:v8gccontroller.cpp%20ObjectGrouperVisitor&sa=N&cd=1&ct=rc

You could either add wrapper for DOMStringMap when visiting Element (JSC requires the way to access the map for the element, so code should be already there) or when visiting the DOMStringMap itself if you have pointer back to the owning element.

Feel free to ping me if you have any questions.
Comment 19 Erik Arvidsson 2010-09-20 17:27:19 PDT
I've been here before but I think that 

        V8DOMWrapper::setHiddenReference(V8DOMWrapper::getWrapper(element), wrapper);

should work. Nate said that getWrapper might return null but since the classList can only be retrieved through the element the wrapper should always be available.

I did look at V8GCController and I could not figure out what to do with that :'(
Comment 20 Erik Arvidsson 2010-09-20 17:27:58 PDT
(In reply to comment #19)
s/classList/dataset/
Comment 21 anton muhin 2010-09-21 03:39:05 PDT
(In reply to comment #19)
> I've been here before but I think that 
> 
>         V8DOMWrapper::setHiddenReference(V8DOMWrapper::getWrapper(element), wrapper);
> 
> should work. Nate said that getWrapper might return null but since the classList can only be retrieved through the element the wrapper should always be available.
> 
> I did look at V8GCController and I could not figure out what to do with that :'(

At least in https://bugs.webkit.org/attachment.cgi?id=67958&action=prettypatch you use V8DOMWrapper::setHiddenWindowReference(element->document()->frame(), wrapper) which makes a reference from global object to the wrapper (but I saw your https://bugs.webkit.org/attachment.cgi?id=67948&action=prettypatch which is better imho)

I think setHiddenReference thing should work right.  And there is no need to do it with object groping if you don't want to :)

Regarding getWrapper.  I don't know for sure, but it looks extremely likely that one can only create a wrapper for dataset when there is a live wrapper for element, so plain getWrapper should work.  To be completely safe you might want to prefer to use toV8 which will create a wrapper if one is missing.
Comment 22 Erik Arvidsson 2010-09-21 08:30:56 PDT
(In reply to comment #21)
> Regarding getWrapper.  I don't know for sure, but it looks extremely likely that one can only create a wrapper for dataset when there is a live wrapper for element, so plain getWrapper should work.  To be completely safe you might want to prefer to use toV8 which will create a wrapper if one is missing.

toV8 creates a V8::Value. This needs a V8::Object.
Comment 23 anton muhin 2010-09-21 09:34:12 PDT
(In reply to comment #22)
> (In reply to comment #21)
> > Regarding getWrapper.  I don't know for sure, but it looks extremely likely that one can only create a wrapper for dataset when there is a live wrapper for element, so plain getWrapper should work.  To be completely safe you might want to prefer to use toV8 which will create a wrapper if one is missing.
> 
> toV8 creates a V8::Value. This needs a V8::Object.

Yes.  That's why we have Cast or As methods :)  But you need to check before it's not empty.

v8::Value corresponds to any JS object (string, number, real object, etc) and v8::Object stands for real objects.  So if you manage to get anything from toV8, it must be object (unless handle is empty).  That probably should have been put into signature.