JSC ignores the extra memory cost of HTMLCollection after a major GC
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 28 Feb 2014 05:53:29 +0000 (05:53 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 28 Feb 2014 05:53:29 +0000 (05:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=129450

Reviewed by Andreas Kling.

Report the extra memory cost of HTMLCollection to JSC.

Unfortunately, the existing mechanism to report the extra memory cost in toJS is insufficient for
HTMLCollection since collection caches are populated later when HTMLCollection is accessed. Also,
the extra memory cost reported by Heap::reportExtraMemoryCost will be thrown away after a major GC.

To work around this limitation, added a visitor.reportExtraMemoryUsage call inside visitChildren
for interfaces with a newly added ReportExtraMemoryCost IDL extension flag to report the extra cost.

Since we may need to generate visitChildren when this flag is set, we can't automatically detect
and generate calls using C++ template as done in r148648.

* CMakeLists.txt:
* GNUmakefile.list.am:
* Modules/webaudio/AudioBuffer.idl:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/JSDOMBinding.h:
* bindings/scripts/CodeGeneratorJS.pm:
(InstanceNeedsVisitChildren):
(GenerateHeader):
(GenerateImplementation):
* bindings/scripts/IDLAttributes.txt:
* bindings/scripts/test/JS/JSTestActiveDOMObject.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestCustomNamedGetter.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestEventConstructor.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestEventTarget.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestException.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestGenerateIsReachable.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestInterface.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestMediaQueryListListener.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestNamedConstructor.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestObj.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestOverloadedConstructors.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestSerializedScriptValueInterface.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSTestTypedefs.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSattribute.cpp:
(WebCore::toJS):
* bindings/scripts/test/JS/JSreadonly.cpp:
(WebCore::toJS):
* dom/ChildNodeList.h:
* dom/CollectionIndexCache.cpp: Added.
(WebCore::reportExtraMemoryCostForCollectionIndexCache):
* dom/CollectionIndexCache.h:
(WebCore::CollectionIndexCache::memoryCost):
(WebCore::NodeType>::CollectionIndexCache):
(WebCore::NodeType>::nodeCount):
(WebCore::NodeType>::computeNodeCountUpdatingListCache):
(WebCore::NodeType>::nodeAt):
(WebCore::NodeType>::invalidate):
* dom/DOMAllInOne.cpp:
* dom/LiveNodeList.cpp:
(WebCore::LiveNodeList::memoryCost):
* dom/LiveNodeList.h:
* dom/NodeList.h:
(WebCore::NodeList::memoryCost):
* dom/NodeList.idl:
* html/HTMLCollection.cpp:
(WebCore::HTMLCollection::updateNamedElementCache):
* html/HTMLCollection.h:
(WebCore::CollectionNamedElementCache::didPopulate):
(WebCore::CollectionNamedElementCache::memoryCost):
(WebCore::CollectionNamedElementCache::find):
(WebCore::HTMLCollection::memoryCost):
* html/HTMLCollection.idl:
* html/HTMLFormControlsCollection.cpp:
(WebCore::HTMLFormControlsCollection::updateNamedElementCache):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@164853 268f45cc-cd09-0410-ab3c-d52691b4dbfc

36 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/Modules/webaudio/AudioBuffer.idl
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/JSDOMBinding.h
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/bindings/scripts/IDLAttributes.txt
Source/WebCore/bindings/scripts/test/JS/JSTestActiveDOMObject.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestCustomNamedGetter.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestEventConstructor.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestEventTarget.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestException.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestGenerateIsReachable.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestInterface.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestMediaQueryListListener.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestNamedConstructor.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestOverloadedConstructors.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestSerializedScriptValueInterface.cpp
Source/WebCore/bindings/scripts/test/JS/JSTestTypedefs.cpp
Source/WebCore/bindings/scripts/test/JS/JSattribute.cpp
Source/WebCore/bindings/scripts/test/JS/JSreadonly.cpp
Source/WebCore/dom/ChildNodeList.h
Source/WebCore/dom/CollectionIndexCache.cpp [new file with mode: 0644]
Source/WebCore/dom/CollectionIndexCache.h
Source/WebCore/dom/DOMAllInOne.cpp
Source/WebCore/dom/LiveNodeList.cpp
Source/WebCore/dom/LiveNodeList.h
Source/WebCore/dom/NodeList.h
Source/WebCore/dom/NodeList.idl
Source/WebCore/html/HTMLCollection.cpp
Source/WebCore/html/HTMLCollection.h
Source/WebCore/html/HTMLCollection.idl
Source/WebCore/html/HTMLFormControlsCollection.cpp

index c4beb9b..b218b6b 100644 (file)
@@ -1125,6 +1125,7 @@ set(WebCore_SOURCES
     dom/ClientRectList.cpp
     dom/Clipboard.cpp
     dom/ClipboardEvent.cpp
+    dom/CollectionIndexCache.cpp
     dom/Comment.cpp
     dom/CompositionEvent.cpp
     dom/ContainerNode.cpp
index 192c356..295dff4 100644 (file)
@@ -1,3 +1,91 @@
+2014-02-27  Ryosuke Niwa  <rniwa@webkit.org>
+
+        JSC ignores the extra memory cost of HTMLCollection after a major GC
+        https://bugs.webkit.org/show_bug.cgi?id=129450
+
+        Reviewed by Andreas Kling.
+
+        Report the extra memory cost of HTMLCollection to JSC.
+
+        Unfortunately, the existing mechanism to report the extra memory cost in toJS is insufficient for
+        HTMLCollection since collection caches are populated later when HTMLCollection is accessed. Also,
+        the extra memory cost reported by Heap::reportExtraMemoryCost will be thrown away after a major GC.
+
+        To work around this limitation, added a visitor.reportExtraMemoryUsage call inside visitChildren
+        for interfaces with a newly added ReportExtraMemoryCost IDL extension flag to report the extra cost.
+
+        Since we may need to generate visitChildren when this flag is set, we can't automatically detect
+        and generate calls using C++ template as done in r148648.
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+        * Modules/webaudio/AudioBuffer.idl:
+        * WebCore.vcxproj/WebCore.vcxproj:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/JSDOMBinding.h:
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (InstanceNeedsVisitChildren):
+        (GenerateHeader):
+        (GenerateImplementation):
+        * bindings/scripts/IDLAttributes.txt:
+        * bindings/scripts/test/JS/JSTestActiveDOMObject.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestCustomNamedGetter.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestEventConstructor.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestEventTarget.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestException.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestGenerateIsReachable.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestInterface.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestMediaQueryListListener.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestNamedConstructor.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestObj.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestOverloadedConstructors.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestSerializedScriptValueInterface.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSTestTypedefs.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSattribute.cpp:
+        (WebCore::toJS):
+        * bindings/scripts/test/JS/JSreadonly.cpp:
+        (WebCore::toJS):
+        * dom/ChildNodeList.h:
+        * dom/CollectionIndexCache.cpp: Added.
+        (WebCore::reportExtraMemoryCostForCollectionIndexCache):
+        * dom/CollectionIndexCache.h:
+        (WebCore::CollectionIndexCache::memoryCost):
+        (WebCore::NodeType>::CollectionIndexCache):
+        (WebCore::NodeType>::nodeCount):
+        (WebCore::NodeType>::computeNodeCountUpdatingListCache):
+        (WebCore::NodeType>::nodeAt):
+        (WebCore::NodeType>::invalidate):
+        * dom/DOMAllInOne.cpp:
+        * dom/LiveNodeList.cpp:
+        (WebCore::LiveNodeList::memoryCost):
+        * dom/LiveNodeList.h:
+        * dom/NodeList.h:
+        (WebCore::NodeList::memoryCost):
+        * dom/NodeList.idl:
+        * html/HTMLCollection.cpp:
+        (WebCore::HTMLCollection::updateNamedElementCache):
+        * html/HTMLCollection.h:
+        (WebCore::CollectionNamedElementCache::didPopulate):
+        (WebCore::CollectionNamedElementCache::memoryCost):
+        (WebCore::CollectionNamedElementCache::find):
+        (WebCore::HTMLCollection::memoryCost):
+        * html/HTMLCollection.idl:
+        * html/HTMLFormControlsCollection.cpp:
+        (WebCore::HTMLFormControlsCollection::updateNamedElementCache):
+
 2014-02-27  Benjamin Poulain  <benjamin@webkit.org>
 
         Compile attribute value matching
index efc94eb..399ea01 100644 (file)
@@ -2757,13 +2757,14 @@ webcore_sources += \
        Source/WebCore/dom/ClipboardEvent.cpp \
        Source/WebCore/dom/ClipboardEvent.h \
        Source/WebCore/dom/Clipboard.h \
+       Source/WebCore/dom/CollectionIndexCache.cpp \
        Source/WebCore/dom/CollectionIndexCache.h \
        Source/WebCore/dom/Comment.cpp \
        Source/WebCore/dom/Comment.h \
        Source/WebCore/dom/CompositionEvent.cpp \
        Source/WebCore/dom/CompositionEvent.h \
-       Source/WebCore/dom/ContainerNodeAlgorithms.h \
        Source/WebCore/dom/ContainerNodeAlgorithms.cpp \
+       Source/WebCore/dom/ContainerNodeAlgorithms.h \
        Source/WebCore/dom/ContainerNode.cpp \
        Source/WebCore/dom/ContainerNode.h \
        Source/WebCore/dom/ContextDestructionObserver.cpp \
index 37a5700..7c44998 100644 (file)
@@ -29,6 +29,7 @@
 [
     Conditional=WEB_AUDIO,
     ImplementationLacksVTable,
+    ReportExtraMemoryCost,
 ] interface AudioBuffer {
     readonly attribute long length; // in sample-frames
     readonly attribute float duration; // in seconds
index 5218b5b..9372696 100644 (file)
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="..\dom\CollectionIndexCache.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="..\dom\Comment.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
index 56bb53f..e15ec5a 100644 (file)
                9BC6C21C13CCC97B008E0337 /* HTMLTextFormControlElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BC6C21A13CCC97B008E0337 /* HTMLTextFormControlElement.cpp */; };
                9BD0BF9312A42BF50072FD43 /* ScopedEventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BD0BF9112A42BF50072FD43 /* ScopedEventQueue.h */; };
                9BD0BF9412A42BF50072FD43 /* ScopedEventQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD0BF9212A42BF50072FD43 /* ScopedEventQueue.cpp */; };
+               9BD8A95A18BEFC7600987E9A /* CollectionIndexCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BD8A95918BEFC7600987E9A /* CollectionIndexCache.cpp */; };
                9BF9A8801648DD2F001C6B23 /* JSHTMLFormControlsCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9BF9A87E1648DD2F001C6B23 /* JSHTMLFormControlsCollection.cpp */; };
                9BF9A8811648DD2F001C6B23 /* JSHTMLFormControlsCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BF9A87F1648DD2F001C6B23 /* JSHTMLFormControlsCollection.h */; };
                9F0D6B2E121BFEBA006C0288 /* InspectorProfilerAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9F0D6B2C121BFEBA006C0288 /* InspectorProfilerAgent.cpp */; };
                9BC6C21A13CCC97B008E0337 /* HTMLTextFormControlElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLTextFormControlElement.cpp; sourceTree = "<group>"; };
                9BD0BF9112A42BF50072FD43 /* ScopedEventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScopedEventQueue.h; sourceTree = "<group>"; };
                9BD0BF9212A42BF50072FD43 /* ScopedEventQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScopedEventQueue.cpp; sourceTree = "<group>"; };
+               9BD8A95918BEFC7600987E9A /* CollectionIndexCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CollectionIndexCache.cpp; sourceTree = "<group>"; };
                9BF9A87E1648DD2F001C6B23 /* JSHTMLFormControlsCollection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSHTMLFormControlsCollection.cpp; sourceTree = "<group>"; };
                9BF9A87F1648DD2F001C6B23 /* JSHTMLFormControlsCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSHTMLFormControlsCollection.h; sourceTree = "<group>"; };
                9F0D6B2C121BFEBA006C0288 /* InspectorProfilerAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorProfilerAgent.cpp; sourceTree = "<group>"; };
                                85031B2A0A44EFC700F992E0 /* ClipboardEvent.h */,
                                2D90660C0665D937006B6F1A /* ClipboardMac.mm */,
                                E425A49918292B840020CFCF /* CollectionIndexCache.h */,
+                               9BD8A95918BEFC7600987E9A /* CollectionIndexCache.cpp */,
                                6550B697099DF0270090D781 /* Comment.cpp */,
                                6550B698099DF0270090D781 /* Comment.h */,
                                85089CC70A98C22600A275AA /* Comment.idl */,
                                97DCE20110807C750057D394 /* HistoryController.cpp in Sources */,
                                51741D120B07259A00ED442C /* HistoryItem.cpp in Sources */,
                                5160F4980B0AA75F00C1D2AF /* HistoryItemMac.mm in Sources */,
+                               9BD8A95A18BEFC7600987E9A /* CollectionIndexCache.cpp in Sources */,
                                4969B0F213D0B33F00DF3521 /* HitTestingTransformState.cpp in Sources */,
                                2D8287F616E4A0380086BD00 /* HitTestLocation.cpp in Sources */,
                                9307F1D70AF2D59000DBA31A /* HitTestResult.cpp in Sources */,
index fb0574c..c8de41b 100644 (file)
@@ -642,19 +642,6 @@ public:
     static const bool value = sizeof(test<T>(0)) == sizeof(YesType);
 };
 
-template <typename T, bool hasReportCostFunction = HasMemoryCostMemberFunction<T>::value > struct ReportMemoryCost;
-template <typename T> struct ReportMemoryCost<T, true> {
-    static void reportMemoryCost(JSC::ExecState* exec, T* impl)
-    {
-        exec->heap()->reportExtraMemoryCost(impl->memoryCost());
-    }
-};
-template <typename T> struct ReportMemoryCost<T, false> {
-    static void reportMemoryCost(JSC::ExecState*, T*)
-    {
-    }
-};
-
 enum SecurityReportingOption {
     DoNotReportSecurityError,
     ReportSecurityError,
index 1221a19..5b9e92d 100644 (file)
@@ -770,6 +770,15 @@ sub InstanceOverridesPutDeclaration
         || $interface->extendedAttributes->{"CustomIndexedSetter"};
 }
 
+sub InstanceNeedsVisitChildren
+{
+    my $interface = shift;
+    return $interface->extendedAttributes->{"JSCustomMarkFunction"}
+    || $interface->extendedAttributes->{"EventTarget"}
+    || $interface->name eq "EventTarget"
+    || $interface->extendedAttributes->{"ReportExtraMemoryCost"};
+}
+
 sub GenerateHeader
 {
     my $object = shift;
@@ -783,7 +792,7 @@ sub GenerateHeader
     my $hasRealParent = $interface->parent;
     my $hasParent = $hasLegacyParent || $hasRealParent;
     my $parentClassName = GetParentClassName($interface);
-    my $needsMarkChildren = $interface->extendedAttributes->{"JSCustomMarkFunction"} || $interface->extendedAttributes->{"EventTarget"} || $interface->name eq "EventTarget";
+    my $needsVisitChildren = InstanceNeedsVisitChildren($interface);
 
     # - Add default header template and header protection
     push(@headerContentHeader, GenerateHeaderContentHeader($interface));
@@ -1005,14 +1014,14 @@ sub GenerateHeader
                 push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
                 push(@headerContent, "    JSC::WriteBarrier<JSC::Unknown> m_" . $attribute->signature->name . ";\n");
                 $numCachedAttributes++;
-                $needsMarkChildren = 1;
+                $needsVisitChildren = 1;
                 push(@headerContent, "#endif\n") if $conditionalString;
             }
         }
     }
 
     # visit function
-    if ($needsMarkChildren) {
+    if ($needsVisitChildren) {
         push(@headerContent, "    static void visitChildren(JSCell*, JSC::SlotVisitor&);\n\n");
         $structureFlags{"JSC::OverridesVisitChildren"} = 1;
     }
@@ -1193,7 +1202,7 @@ sub GenerateHeader
         push(@headerContent, "    static bool getOwnPropertySlot(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&);\n");
         $structureFlags{"JSC::OverridesGetOwnPropertySlot"} = 1;
     }
-    if ($interface->extendedAttributes->{"JSCustomMarkFunction"} or $needsMarkChildren) {
+    if ($interface->extendedAttributes->{"JSCustomMarkFunction"} or $needsVisitChildren) {
         $structureFlags{"JSC::OverridesVisitChildren"} = 1;
     }
     push(@headerContent,
@@ -1690,7 +1699,7 @@ sub GenerateImplementation
     my $parentClassName = GetParentClassName($interface);
     my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface);
     my $eventTarget = $interface->extendedAttributes->{"EventTarget"} || ($codeGenerator->InheritsInterface($interface, "EventTarget") && $interface->name ne "EventTarget");
-    my $needsMarkChildren = $interface->extendedAttributes->{"JSCustomMarkFunction"} || $interface->extendedAttributes->{"EventTarget"} || $interface->name eq "EventTarget";
+    my $needsVisitChildren = InstanceNeedsVisitChildren($interface);
 
     my $namedGetterFunction = GetNamedGetterFunction($interface);
     my $indexedGetterFunction = GetIndexedGetterFunction($interface);
@@ -2162,7 +2171,7 @@ sub GenerateImplementation
             }
 
             if ($attribute->signature->extendedAttributes->{"CachedAttribute"}) {
-                $needsMarkChildren = 1;
+                $needsVisitChildren = 1;
             }
 
             if ($interface->extendedAttributes->{"CheckSecurity"} &&
@@ -2746,7 +2755,7 @@ sub GenerateImplementation
 
     }
 
-    if ($needsMarkChildren && !$interface->extendedAttributes->{"JSCustomMarkFunction"}) {
+    if ($needsVisitChildren && !$interface->extendedAttributes->{"JSCustomMarkFunction"}) {
         push(@implContent, "void ${className}::visitChildren(JSCell* cell, SlotVisitor& visitor)\n");
         push(@implContent, "{\n");
         push(@implContent, "    ${className}* thisObject = jsCast<${className}*>(cell);\n");
@@ -2757,6 +2766,9 @@ sub GenerateImplementation
         if ($interface->extendedAttributes->{"EventTarget"} || $interface->name eq "EventTarget") {
             push(@implContent, "    thisObject->impl().visitJSEventListeners(visitor);\n");
         }
+        if ($interface->extendedAttributes->{"ReportExtraMemoryCost"}) {
+            push(@implContent, "    visitor.reportExtraMemoryUsage(cell, thisObject->impl().memoryCost());\n");
+        }
         if ($numCachedAttributes > 0) {
             foreach (@{$interface->attributes}) {
                 my $attribute = $_;
@@ -3019,8 +3031,8 @@ END
     COMPILE_ASSERT(!__is_polymorphic($implType), ${implType}_is_polymorphic_but_idl_claims_not_to_be);
 #endif
 END
-        push(@implContent, <<END);
-    ReportMemoryCost<$implType>::reportMemoryCost(exec, impl);
+        push(@implContent, <<END) if $interface->extendedAttributes->{"ReportExtraMemoryCost"};
+    exec->heap()->reportExtraMemoryCost(impl->memoryCost());
 END
 
         if ($svgPropertyType) {
index 7c55cc7..5e793a5 100644 (file)
@@ -98,6 +98,7 @@ RaisesException
 Reflect=*
 Replaceable
 ReplaceableConstructor
+ReportExtraMemoryCost
 ReturnNewObject
 SetterRaisesException
 SkipVTableValidation
index 16ac5f0..3cfbffe 100644 (file)
@@ -253,7 +253,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestAct
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestActiveDOMObject>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestActiveDOMObject>(exec, globalObject, impl);
 }
 
index 529069b..76e6f21 100644 (file)
@@ -224,7 +224,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestCus
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestCustomNamedGetter>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestCustomNamedGetter>(exec, globalObject, impl);
 }
 
index 8d7e619..cf73b6d 100644 (file)
@@ -267,7 +267,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestEve
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestEventConstructor>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestEventConstructor>(exec, globalObject, impl);
 }
 
index cb9b5ec..774462c 100644 (file)
@@ -333,7 +333,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestEve
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestEventTarget>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestEventTarget>(exec, globalObject, impl);
 }
 
index 84fd569..c4a8497 100644 (file)
@@ -209,7 +209,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestExc
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestException>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestException>(exec, globalObject, impl);
 }
 
index 92e286c..0a3868d 100644 (file)
@@ -187,7 +187,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestGen
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestGenerateIsReachable>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestGenerateIsReachable>(exec, globalObject, impl);
 }
 
index a046855..0c530f5 100644 (file)
@@ -845,7 +845,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestInt
     // attribute to TestInterface.
     COMPILE_ASSERT(!__is_polymorphic(TestInterface), TestInterface_is_polymorphic_but_idl_claims_not_to_be);
 #endif
-    ReportMemoryCost<TestInterface>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestInterface>(exec, globalObject, impl);
 }
 
index 90402ea..6f33e85 100644 (file)
@@ -200,7 +200,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestMed
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestMediaQueryListListener>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestMediaQueryListListener>(exec, globalObject, impl);
 }
 
index e4d7a80..b544986 100644 (file)
@@ -232,7 +232,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestNam
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestNamedConstructor>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestNamedConstructor>(exec, globalObject, impl);
 }
 
index 216c1d8..25cafcc 100644 (file)
@@ -4215,7 +4215,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestObj
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestObj>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestObj>(exec, globalObject, impl);
 }
 
index 74a59eb..0c0e9fd 100644 (file)
@@ -253,7 +253,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestOve
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestOverloadedConstructors>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestOverloadedConstructors>(exec, globalObject, impl);
 }
 
index ddf5221..434a983 100644 (file)
@@ -337,7 +337,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestSer
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestSerializedScriptValueInterface>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestSerializedScriptValueInterface>(exec, globalObject, impl);
 }
 
index b6bcbb1..204acaa 100644 (file)
@@ -698,7 +698,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, TestTyp
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<TestTypedefs>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSTestTypedefs>(exec, globalObject, impl);
 }
 
index d508550..3a8ef18 100644 (file)
@@ -201,7 +201,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, attribu
     // by adding the SkipVTableValidation attribute to the interface IDL definition
     RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer);
 #endif
-    ReportMemoryCost<attribute>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSattribute>(exec, globalObject, impl);
 }
 
index fbd923c..d6789b7 100644 (file)
@@ -158,7 +158,6 @@ JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, readonl
     // attribute to readonly.
     COMPILE_ASSERT(!__is_polymorphic(readonly), readonly_is_polymorphic_but_idl_claims_not_to_be);
 #endif
-    ReportMemoryCost<readonly>::reportMemoryCost(exec, impl);
     return createNewWrapper<JSreadonly>(exec, globalObject, impl);
 }
 
index 2454e8e..0a26203 100644 (file)
@@ -49,6 +49,7 @@ private:
     virtual unsigned length() const override { return 0; }
     virtual Node* item(unsigned) const override { return nullptr; }
     virtual Node* namedItem(const AtomicString&) const override { return nullptr; }
+    virtual size_t memoryCost() const override { return 0; }
 
     virtual bool isEmptyNodeList() const override { return true; }
 
@@ -81,6 +82,7 @@ private:
     virtual unsigned length() const override;
     virtual Node* item(unsigned index) const override;
     virtual Node* namedItem(const AtomicString&) const override;
+    virtual size_t memoryCost() const override { return m_indexCache.memoryCost(); }
 
     virtual bool isChildNodeList() const override { return true; }
 
diff --git a/Source/WebCore/dom/CollectionIndexCache.cpp b/Source/WebCore/dom/CollectionIndexCache.cpp
new file mode 100644 (file)
index 0000000..1cd0063
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CollectionIndexCache.h"
+
+#include "JSDOMWindowBase.h"
+
+namespace WebCore {
+
+void reportExtraMemoryCostForCollectionIndexCache(size_t cost)
+{
+    JSC::VM* vm = JSDOMWindowBase::commonVM();
+    JSC::JSLockHolder lock(vm);
+    vm->heap.reportExtraMemoryCost(cost);
+}
+
+}
index 8a761f9..2d98c6f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #ifndef CollectionIndexCache_h
 #define CollectionIndexCache_h
 
+#include <wtf/Vector.h>
+
 namespace WebCore {
 
+void reportExtraMemoryCostForCollectionIndexCache(size_t);
+
 template <class Collection, class NodeType>
 class CollectionIndexCache {
 public:
@@ -37,15 +41,19 @@ public:
     NodeType* nodeAt(const Collection&, unsigned index);
 
     void invalidate();
+    size_t memoryCost() { return m_cachedList.capacity() * sizeof(NodeType*); }
 
 private:
+    unsigned computeNodeCountUpdatingListCache(const Collection&);
     NodeType* nodeBeforeCached(const Collection&, unsigned);
     NodeType* nodeAfterCached(const Collection&, unsigned);
 
     NodeType* m_currentNode;
     unsigned m_currentIndex;
     unsigned m_nodeCount;
-    bool m_nodeCountValid;
+    Vector<NodeType*> m_cachedList;
+    bool m_nodeCountValid : 1;
+    bool m_listValid : 1;
 };
 
 template <class Collection, class NodeType>
@@ -54,6 +62,7 @@ inline CollectionIndexCache<Collection, NodeType>::CollectionIndexCache()
     , m_currentIndex(0)
     , m_nodeCount(0)
     , m_nodeCountValid(false)
+    , m_listValid(false)
 {
 }
 
@@ -61,12 +70,7 @@ template <class Collection, class NodeType>
 inline unsigned CollectionIndexCache<Collection, NodeType>::nodeCount(const Collection& collection)
 {
     if (!m_nodeCountValid) {
-        if (auto first = collection.collectionFirst()) {
-            unsigned count;
-            collection.collectionTraverseForward(*first, std::numeric_limits<unsigned>::max(), count);
-            m_nodeCount = count + 1;
-        } else
-            m_nodeCount = 0;
+        m_nodeCount = computeNodeCountUpdatingListCache(collection);
         m_nodeCountValid = true;
     }
 
@@ -74,6 +78,29 @@ inline unsigned CollectionIndexCache<Collection, NodeType>::nodeCount(const Coll
 }
 
 template <class Collection, class NodeType>
+unsigned CollectionIndexCache<Collection, NodeType>::computeNodeCountUpdatingListCache(const Collection& collection)
+{
+    NodeType* first = collection.collectionFirst();
+    if (!first)
+        return 0;
+
+    unsigned oldCapacity = m_cachedList.capacity();
+    NodeType* currentNode = first;
+    while (currentNode) {
+        m_cachedList.append(currentNode);
+        unsigned traversed;
+        currentNode = collection.collectionTraverseForward(*currentNode, 1, traversed);
+        ASSERT(traversed == (currentNode ? 1 : 0));
+    }
+    m_listValid = true;
+
+    if (unsigned capacityDifference = m_cachedList.capacity() - oldCapacity)
+        reportExtraMemoryCostForCollectionIndexCache(capacityDifference * sizeof(NodeType*));
+
+    return m_cachedList.size();
+}
+
+template <class Collection, class NodeType>
 inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeBeforeCached(const Collection& collection, unsigned index)
 {
     ASSERT(m_currentNode);
@@ -133,6 +160,9 @@ inline NodeType* CollectionIndexCache<Collection, NodeType>::nodeAt(const Collec
     if (m_nodeCountValid && index >= m_nodeCount)
         return nullptr;
 
+    if (m_listValid)
+        return m_cachedList[index];
+
     if (m_currentNode) {
         if (index > m_currentIndex)
             return nodeAfterCached(collection, index);
@@ -165,8 +195,11 @@ void CollectionIndexCache<Collection, NodeType>::invalidate()
 {
     m_currentNode = nullptr;
     m_nodeCountValid = false;
+    m_listValid = false;
+    m_cachedList.shrink(0);
 }
 
+
 }
 
 #endif
index 8772b9f..4c0bb10 100644 (file)
@@ -39,6 +39,7 @@
 #include "ClientRectList.cpp"
 #include "Clipboard.cpp"
 #include "ClipboardEvent.cpp"
+#include "CollectionIndexCache.cpp"
 #include "Comment.cpp"
 #include "CompositionEvent.cpp"
 #include "ContainerNode.cpp"
index 5a0dc22..2a2864a 100644 (file)
@@ -143,6 +143,11 @@ Node* LiveNodeList::item(unsigned offset) const
     return m_indexCache.nodeAt(*this, offset);
 }
 
+size_t LiveNodeList::memoryCost() const
+{
+    return m_indexCache.memoryCost();
+}
+
 void LiveNodeList::invalidateCache() const
 {
     m_indexCache.invalidate();
index 7cbc1b9..b2940ae 100644 (file)
@@ -77,6 +77,7 @@ public:
     // DOM API
     virtual unsigned length() const override final;
     virtual Node* item(unsigned offset) const override final;
+    virtual size_t memoryCost() const override;
 
     ALWAYS_INLINE bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument; }
     ALWAYS_INLINE NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); }
index cd82115..3221952 100644 (file)
@@ -45,6 +45,8 @@ public:
     virtual bool isLiveNodeList() const { return false; }
     virtual bool isChildNodeList() const { return false; }
     virtual bool isEmptyNodeList() const { return false; }
+    virtual size_t memoryCost() const { return 0; }
+
 };
 
 } // namespace WebCore
index 2d419bf..5ac4974 100644 (file)
@@ -22,6 +22,7 @@
     CustomIsReachable,
     JSCustomGetOwnPropertySlotAndDescriptor,
     SkipVTableValidation,
+    ReportExtraMemoryCost,
 ] interface NodeList {
 
     getter Node item(unsigned long index);
index b0c9916..34dff42 100644 (file)
@@ -444,6 +444,10 @@ void HTMLCollection::updateNamedElementCache() const
         if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (type() != DocAll || nameShouldBeVisibleInDocumentAll(toHTMLElement(*element))))
             cache.appendNameCache(nameAttrVal, element);
     }
+
+    m_indexCache.nodeCount(*this);
+
+    cache.didPopulate();
 }
 
 bool HTMLCollection::hasNamedItem(const AtomicString& name) const
index 2d210b7..c9ad372 100644 (file)
@@ -40,17 +40,34 @@ class Element;
 
 class CollectionNamedElementCache {
 public:
+#ifndef ASSERT_DISABLED
+    CollectionNamedElementCache : m_didPopulateCalled(false) { }
+#endif
+
     const Vector<Element*>* findElementsWithId(const AtomicString& id) const { return find(m_idToElementsMap, id); }
     const Vector<Element*>* findElementsWithName(const AtomicString& name) const { return find(m_nameToElementsMap, name); }
 
     void appendIdCache(const AtomicString& id, Element* element) { return append(m_idToElementsMap, id, element); }
     void appendNameCache(const AtomicString& name, Element* element)  { return append(m_nameToElementsMap, name, element); }
 
+    void didPopulate()
+    {
+#ifndef ASSERT_DISABLED
+        m_didPopulateCalled = true;
+#endif
+        if (size_t cost = memoryCost())
+            reportExtraMemoryCostForCollectionIndexCache(cost);
+    }
+    size_t memoryCost() const { return (m_idToElementsMap.size() + m_nameToElementsMap.size()) * sizeof(Element*); }
+
 private:
     typedef HashMap<AtomicStringImpl*, Vector<Element*>> StringToElementsMap;
 
-    static const Vector<Element*>* find(const StringToElementsMap& map, const AtomicString& key)
+    const Vector<Element*>* find(const StringToElementsMap& map, const AtomicString& key) const
     {
+#ifndef ASSERT_DISABLED
+        ASSERT(m_didPopulateCalled);
+#endif
         auto it = map.find(key.impl());
         return it != map.end() ? &it->value : nullptr;
     }
@@ -62,6 +79,9 @@ private:
 
     StringToElementsMap m_idToElementsMap;
     StringToElementsMap m_nameToElementsMap;
+#ifndef ASSERT_DISABLED
+    bool m_didPopulateCalled;
+#endif
 };
 
 class HTMLCollection : public ScriptWrappable, public RefCounted<HTMLCollection> {
@@ -78,6 +98,7 @@ public:
     // Non-DOM API
     bool hasNamedItem(const AtomicString& name) const;
     void namedItems(const AtomicString& name, Vector<Ref<Element>>&) const;
+    size_t memoryCost() const { return m_indexCache.memoryCost() + (m_namedElementCache ? m_namedElementCache->memoryCost() : 0); }
 
     bool isRootedAtDocument() const { return m_rootType == NodeListIsRootedAtDocument; }
     NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); }
index 62cb050..b3e78fb 100644 (file)
@@ -22,6 +22,7 @@
     CustomToJSObject,
     GenerateIsReachable=ImplOwnerNodeRoot,
     ObjCPolymorphic,
+    ReportExtraMemoryCost,
 ] interface HTMLCollection {
     readonly attribute unsigned long length;
     getter Node item([Default=Undefined] optional unsigned long index);
index 3ecd885..a52937e 100644 (file)
@@ -173,6 +173,8 @@ void HTMLFormControlsCollection::updateNamedElementCache() const
                 cache.appendNameCache(nameAttrVal, &element);
         }
     }
+
+    cache.didPopulate();
 }
 
 void HTMLFormControlsCollection::invalidateCache() const