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 c4beb9b92e428c3f7d1f3778b7d601d09b01175b..b218b6be1f7c2f1e2cdfe4f77e23268c3ed6efe3 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 192c3566da32ddc9bfb4fba5bb4c40e12c704ae2..295dff45aaf17c267ae1d3257933670001b455bc 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 efc94eb71c6a4a5450765ce001654ccd8641e9b2..399ea010de0462e680c551ff6482d425e109249a 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 37a570040a41449ee498a62283a1b897b7179209..7c44998b11b92c61294f82cb2b468f4eab903c2c 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 5218b5bb895d454d7964ffde0d30303f93e9c6f1..93726963e01cdcb81fc1e69ba0d6002efd28bcc2 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 56bb53fb32a8a47468c57faf437d70001c53ad7f..e15ec5a422acff8a32bb01ed1e8413d034f2bc00 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 fb0574caa07789ce1c2eb4fbab5d65e14193623d..c8de41bb3be4be2b6b76793034f80707f78e77f3 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 1221a19558dd65bef9ab9aea4a4d293eebd6e140..5b9e92d3b04a977c7acf8950fd0696c736cec761 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 7c55cc73965f39c7795d27f83843e13e4abc3b30..5e793a5c1c058200b5fbd509a8b177be809083bb 100644 (file)
@@ -98,6 +98,7 @@ RaisesException
 Reflect=*
 Replaceable
 ReplaceableConstructor
+ReportExtraMemoryCost
 ReturnNewObject
 SetterRaisesException
 SkipVTableValidation
index 16ac5f033aeac27d96e94097d713995c58890374..3cfbffec4f0473fb34fab356a0c49930fb3eaceb 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 529069b441c05153a4d786b4230c7b25b7adc0eb..76e6f21f734b0a8226887040394d2d543e814538 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 8d7e619b8db2401e920aeaaf7ff1d296fb82924d..cf73b6d03b50b019b92d0ca22cbf203f236848e0 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 cb9b5ec49a791f107ffaff74839f1ea86521c33c..774462c0c87a1a0d4b1849eac07a993970c715e6 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 84fd5694b52eeed39128fd00506af77e5a8e1207..c4a84974e639bc4a437da7db12156eddc05cc92e 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 92e286c49c9ac1c47025b6786b2763c22c788b8e..0a3868d6ca50cc2af749cdbf1c515c46b2e2a2da 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 a046855661a3846e5cc763f1d855a29b2f990d48..0c530f5133fbb1b8650d7981d74de1dabf7e6431 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 90402ead29a8e07d9dab1ed7163e9c0ce1d133e4..6f33e85d7100f4ad5965a4ef8e08fc94029d3704 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 e4d7a806a1f65a96b47796b96856f208dc177303..b5449860398193b10f0431fa1a3663e42f844ba5 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 216c1d8f1eda546749f0626929225a5f0c14ae23..25cafcc1c9be194f0657a83d4f67c7adba44112f 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 74a59ebfd7d8dc33fffd3862327b23c986a77d28..0c0e9fd49a26b29f647de18ffde015143669581f 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 ddf522132ab08bc55f12a311146a440bf0516b29..434a9838beb5a283a119b651889116156d0cb117 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 b6bcbb14455d8917040bd2a2a9e26fa9ad430507..204acaabd0c9350cafb626f1cd323848f40c336d 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 d5085504c10f30e9d2642a37e13b83605b799e0f..3a8ef182b9b447834f25bc7fc56e0a7942dda42d 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 fbd923c343f18296c55cb53c52e014b13dd5783a..d6789b76d139012aca98daf37f089ae30a868aab 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 2454e8e0c6f30b2b7284262d2a93f9e958b04fe8..0a262030fe9fd152b0395793d9af3951c985f21e 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 8a761f9fb58b4658d998fb16584497a1b00dff22..2d98c6fe9fadedda07eec67b0bf5a14549d68a61 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,18 +70,36 @@ 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;
     }
 
     return m_nodeCount;
 }
 
+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)
 {
@@ -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 8772b9f4c9710ce0dec7d9cdb45c98a614fe8faa..4c0bb10b8837bed650f36d8aba730dbc7c03fcda 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 5a0dc22ad4d8c8b27928f12e5fb8a38ed2e257f3..2a2864a935e4c9fc5e7846cad359007b58939a50 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 7cbc1b964ff6dbfc6f71d3f57c674341d272f846..b2940ae1a42268589b3595bf58f3d50eef1d629c 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 cd82115fdeb976985703f8c72d587484cc42d051..322195279b6e683acfc81a61297a4f370615367d 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 2d419bf73f4d3acec960a9d5e0ce4f7207c51baf..5ac4974087e9352b846cbb41a250f4062b0e6834 100644 (file)
@@ -22,6 +22,7 @@
     CustomIsReachable,
     JSCustomGetOwnPropertySlotAndDescriptor,
     SkipVTableValidation,
+    ReportExtraMemoryCost,
 ] interface NodeList {
 
     getter Node item(unsigned long index);
index b0c991633900b9525b6796508734b0d58a0030da..34dff4278de787861781e030b2cbd3a20d1c3e53 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 2d210b76ecf096f486b67b487247b350ba17f388..c9ad372057deaa5f20e521f5923c04c3f496088f 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 62cb050aed9c35e7fd7bd30761365fc63d6eab03..b3e78fb10b394195d4f7b8fc07e2213c2895c5dc 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 3ecd885cc3bd3cc7a67a7928fc1112d926d71c54..a52937ee23446cb1beceffbc324100a39f5e545b 100644 (file)
@@ -173,6 +173,8 @@ void HTMLFormControlsCollection::updateNamedElementCache() const
                 cache.appendNameCache(nameAttrVal, &element);
         }
     }
+
+    cache.didPopulate();
 }
 
 void HTMLFormControlsCollection::invalidateCache() const