Title: [270414] trunk
Revision
270414
Author
[email protected]
Date
2020-12-03 16:01:23 -0800 (Thu, 03 Dec 2020)

Log Message

Serialize NFA to disk before converting it to a DFA when compiling a WKContentRuleList
https://bugs.webkit.org/show_bug.cgi?id=219452

Patch by Alex Christensen <[email protected]> on 2020-12-03
Reviewed by Geoffrey Garen.

Source/WebCore:

This decreases maximum memory use by about 50% because the NFA and DFA never need to be in memory at the same time.
I'll have to do some tuning and on-device measurement, but this may allow us to increase maxRuleCount.

* Headers.cmake:
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* contentextensions/CombinedURLFilters.cpp:
(WebCore::ContentExtensions::CombinedURLFilters::processNFAs):
* contentextensions/CombinedURLFilters.h:
* contentextensions/ContentExtensionCompiler.cpp:
(WebCore::ContentExtensions::compileToBytecode):
(WebCore::ContentExtensions::compileRuleList):
* contentextensions/ContentExtensionError.cpp:
(WebCore::ContentExtensions::contentExtensionErrorCategory):
* contentextensions/ContentExtensionError.h:
* contentextensions/ContentExtensionsDebugging.h:
* contentextensions/DFA.cpp:
(WebCore::ContentExtensions::DFA::shrinkToFit): Deleted.
* contentextensions/DFA.h:
* contentextensions/ImmutableNFA.h:
(WebCore::ContentExtensions::ImmutableNFA::clear):
(WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator* const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator-> const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator== const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator!= const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator++): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::IterableConstTargets::begin const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::IterableConstTargets::end const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator== const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator!= const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator++): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::first const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::last const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::data const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::range const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::IterableConstRange::begin const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::IterableConstRange::end const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::IterableConstRange::debugPrint const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::transitionsForNode const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::root const): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::finalize): Deleted.
* contentextensions/ImmutableNFANodeBuilder.h:
* contentextensions/NFAToDFA.cpp:
(WebCore::ContentExtensions::epsilonClosureExcludingSelf):
(WebCore::ContentExtensions::resolveEpsilonClosures):
(WebCore::ContentExtensions::NodeIdSetToUniqueNodeIdSetSource::NodeIdSetToUniqueNodeIdSetSource):
(WebCore::ContentExtensions::NodeIdSetToUniqueNodeIdSetTranslator::translate):
(WebCore::ContentExtensions::createCombinedTransition):
(WebCore::ContentExtensions::getOrCreateDFANode):
(WebCore::ContentExtensions::NFAToDFA::convert):
* contentextensions/NFAToDFA.h:
* contentextensions/SerializedNFA.cpp: Added.
(WebCore::ContentExtensions::writeAllToFile):
(WebCore::ContentExtensions::SerializedNFA::serialize):
(WebCore::ContentExtensions::SerializedNFA::SerializedNFA):
(WebCore::ContentExtensions::SerializedNFA::pointerAtOffsetInFile const):
(WebCore::ContentExtensions::SerializedNFA::nodes const const):
(WebCore::ContentExtensions::SerializedNFA::transitions const const):
(WebCore::ContentExtensions::SerializedNFA::targets const const):
(WebCore::ContentExtensions::SerializedNFA::epsilonTransitionsTargets const const):
(WebCore::ContentExtensions::SerializedNFA::actions const const):
* contentextensions/SerializedNFA.h: Copied from Source/WebCore/contentextensions/ImmutableNFA.h.
(WebCore::ContentExtensions::SerializedNFA::Range::Range):
(WebCore::ContentExtensions::SerializedNFA::Range::begin const):
(WebCore::ContentExtensions::SerializedNFA::Range::end const):
(WebCore::ContentExtensions::SerializedNFA::Range::size const):
(WebCore::ContentExtensions::SerializedNFA::Range::operator[] const):
(WebCore::ContentExtensions::SerializedNFA::root const):
(WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator* const):
(WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator-> const):
(WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator== const):
(WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator!= const):
(WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator++):
(WebCore::ContentExtensions::SerializedNFA::IterableConstTargets::begin const):
(WebCore::ContentExtensions::SerializedNFA::IterableConstTargets::end const):
(WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::operator== const):
(WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::operator!= const):
(WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::operator++):
(WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::first const):
(WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::last const):
(WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::data const):
(WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::range const):
(WebCore::ContentExtensions::SerializedNFA::IterableConstRange::begin const):
(WebCore::ContentExtensions::SerializedNFA::IterableConstRange::end const):
(WebCore::ContentExtensions::SerializedNFA::IterableConstRange::debugPrint const):
(WebCore::ContentExtensions::SerializedNFA::transitionsForNode const):

Source/WebKit:

* UIProcess/API/Cocoa/WKContentRuleListStore.mm:
(-[WKContentRuleListStore _compileContentRuleListForIdentifier:encodedContentRuleList:completionHandler:]):
* UIProcess/API/Cocoa/WKContentRuleListStorePrivate.h:
Remove NS_RELEASES_ARGUMENT because it was incorrect and unnecessary because the WTF::String is copied to a background thread.

Tools:

Update syntax of existing tests, which cover behavior quite well.

* TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
(TestWebKitAPI::createNFAs):
(TestWebKitAPI::TEST_F):
* TestWebKitAPI/Tests/WebCore/DFAHelpers.h:
(TestWebKitAPI::createNFAs):
(TestWebKitAPI::buildDFAFromPatterns):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (270413 => 270414)


--- trunk/Source/WebCore/ChangeLog	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/ChangeLog	2020-12-04 00:01:23 UTC (rev 270414)
@@ -1,3 +1,97 @@
+2020-12-03  Alex Christensen  <[email protected]>
+
+        Serialize NFA to disk before converting it to a DFA when compiling a WKContentRuleList
+        https://bugs.webkit.org/show_bug.cgi?id=219452
+
+        Reviewed by Geoffrey Garen.
+
+        This decreases maximum memory use by about 50% because the NFA and DFA never need to be in memory at the same time.
+        I'll have to do some tuning and on-device measurement, but this may allow us to increase maxRuleCount.
+
+        * Headers.cmake:
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * contentextensions/CombinedURLFilters.cpp:
+        (WebCore::ContentExtensions::CombinedURLFilters::processNFAs):
+        * contentextensions/CombinedURLFilters.h:
+        * contentextensions/ContentExtensionCompiler.cpp:
+        (WebCore::ContentExtensions::compileToBytecode):
+        (WebCore::ContentExtensions::compileRuleList):
+        * contentextensions/ContentExtensionError.cpp:
+        (WebCore::ContentExtensions::contentExtensionErrorCategory):
+        * contentextensions/ContentExtensionError.h:
+        * contentextensions/ContentExtensionsDebugging.h:
+        * contentextensions/DFA.cpp:
+        (WebCore::ContentExtensions::DFA::shrinkToFit): Deleted.
+        * contentextensions/DFA.h:
+        * contentextensions/ImmutableNFA.h:
+        (WebCore::ContentExtensions::ImmutableNFA::clear):
+        (WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator* const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator-> const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator== const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator!= const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstTargetIterator::operator++): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::IterableConstTargets::begin const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::IterableConstTargets::end const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator== const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator!= const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator++): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::first const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::last const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::data const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::range const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::IterableConstRange::begin const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::IterableConstRange::end const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::IterableConstRange::debugPrint const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::transitionsForNode const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::root const): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::finalize): Deleted.
+        * contentextensions/ImmutableNFANodeBuilder.h:
+        * contentextensions/NFAToDFA.cpp:
+        (WebCore::ContentExtensions::epsilonClosureExcludingSelf):
+        (WebCore::ContentExtensions::resolveEpsilonClosures):
+        (WebCore::ContentExtensions::NodeIdSetToUniqueNodeIdSetSource::NodeIdSetToUniqueNodeIdSetSource):
+        (WebCore::ContentExtensions::NodeIdSetToUniqueNodeIdSetTranslator::translate):
+        (WebCore::ContentExtensions::createCombinedTransition):
+        (WebCore::ContentExtensions::getOrCreateDFANode):
+        (WebCore::ContentExtensions::NFAToDFA::convert):
+        * contentextensions/NFAToDFA.h:
+        * contentextensions/SerializedNFA.cpp: Added.
+        (WebCore::ContentExtensions::writeAllToFile):
+        (WebCore::ContentExtensions::SerializedNFA::serialize):
+        (WebCore::ContentExtensions::SerializedNFA::SerializedNFA):
+        (WebCore::ContentExtensions::SerializedNFA::pointerAtOffsetInFile const):
+        (WebCore::ContentExtensions::SerializedNFA::nodes const const):
+        (WebCore::ContentExtensions::SerializedNFA::transitions const const):
+        (WebCore::ContentExtensions::SerializedNFA::targets const const):
+        (WebCore::ContentExtensions::SerializedNFA::epsilonTransitionsTargets const const):
+        (WebCore::ContentExtensions::SerializedNFA::actions const const):
+        * contentextensions/SerializedNFA.h: Copied from Source/WebCore/contentextensions/ImmutableNFA.h.
+        (WebCore::ContentExtensions::SerializedNFA::Range::Range):
+        (WebCore::ContentExtensions::SerializedNFA::Range::begin const):
+        (WebCore::ContentExtensions::SerializedNFA::Range::end const):
+        (WebCore::ContentExtensions::SerializedNFA::Range::size const):
+        (WebCore::ContentExtensions::SerializedNFA::Range::operator[] const):
+        (WebCore::ContentExtensions::SerializedNFA::root const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator* const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator-> const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator== const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator!= const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstTargetIterator::operator++):
+        (WebCore::ContentExtensions::SerializedNFA::IterableConstTargets::begin const):
+        (WebCore::ContentExtensions::SerializedNFA::IterableConstTargets::end const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::operator== const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::operator!= const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::operator++):
+        (WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::first const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::last const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::data const):
+        (WebCore::ContentExtensions::SerializedNFA::ConstRangeIterator::range const):
+        (WebCore::ContentExtensions::SerializedNFA::IterableConstRange::begin const):
+        (WebCore::ContentExtensions::SerializedNFA::IterableConstRange::end const):
+        (WebCore::ContentExtensions::SerializedNFA::IterableConstRange::debugPrint const):
+        (WebCore::ContentExtensions::SerializedNFA::transitionsForNode const):
+
 2020-12-03  Tim Horton  <[email protected]>
 
         ASSERTION FAILED: isMainThread() in WTF::Optional<IntSize> &WebCore::surfaceMaximumSize()

Modified: trunk/Source/WebCore/Headers.cmake (270413 => 270414)


--- trunk/Source/WebCore/Headers.cmake	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/Headers.cmake	2020-12-04 00:01:23 UTC (rev 270414)
@@ -323,6 +323,7 @@
     contentextensions/NFA.h
     contentextensions/NFANode.h
     contentextensions/NFAToDFA.h
+    contentextensions/SerializedNFA.h
     contentextensions/Term.h
     contentextensions/URLFilterParser.h
 

Modified: trunk/Source/WebCore/Sources.txt (270413 => 270414)


--- trunk/Source/WebCore/Sources.txt	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/Sources.txt	2020-12-04 00:01:23 UTC (rev 270414)
@@ -665,6 +665,7 @@
 contentextensions/DFANode.cpp
 contentextensions/NFA.cpp
 contentextensions/NFAToDFA.cpp
+contentextensions/SerializedNFA.cpp
 contentextensions/URLFilterParser.cpp
 crypto/CryptoAlgorithm.cpp
 crypto/CryptoAlgorithmRegistry.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (270413 => 270414)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2020-12-04 00:01:23 UTC (rev 270414)
@@ -9408,6 +9408,8 @@
 		5B7AB9F62567DB7E006592D0 /* ScrollSnapOffsetsInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollSnapOffsetsInfo.cpp; sourceTree = "<group>"; };
 		5C001521250011000094AA93 /* TextCodecSingleByte.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextCodecSingleByte.cpp; sourceTree = "<group>"; };
 		5C001523250011010094AA93 /* TextCodecSingleByte.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCodecSingleByte.h; sourceTree = "<group>"; };
+		5C2EBE012577198900D55B05 /* SerializedNFA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SerializedNFA.h; sourceTree = "<group>"; };
+		5C2EBE0325771A4C00D55B05 /* SerializedNFA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerializedNFA.cpp; sourceTree = "<group>"; };
 		5C39305D1AA0F6A90029C816 /* DFABytecode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DFABytecode.h; sourceTree = "<group>"; };
 		5C39305E1AA0F6A90029C816 /* DFABytecodeCompiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DFABytecodeCompiler.cpp; sourceTree = "<group>"; };
 		5C39305F1AA0F6A90029C816 /* DFABytecodeCompiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DFABytecodeCompiler.h; sourceTree = "<group>"; };
@@ -18898,6 +18900,8 @@
 				269397201A4A412F00E8349D /* NFANode.h */,
 				267725FA1A5B3AD9003C24DD /* NFAToDFA.cpp */,
 				267725FB1A5B3AD9003C24DD /* NFAToDFA.h */,
+				5C2EBE0325771A4C00D55B05 /* SerializedNFA.cpp */,
+				5C2EBE012577198900D55B05 /* SerializedNFA.h */,
 				26E944DC1AC4B4EA007B85B5 /* Term.h */,
 				267726021A5DF6F2003C24DD /* URLFilterParser.cpp */,
 				267726031A5DF6F2003C24DD /* URLFilterParser.h */,

Modified: trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp	2020-12-04 00:01:23 UTC (rev 270414)
@@ -444,7 +444,7 @@
     clearReverseSuffixTree(reverseSuffixTreeRoots);
 }
 
-void CombinedURLFilters::processNFAs(size_t maxNFASize, const WTF::Function<void(NFA&&)>& handler)
+bool CombinedURLFilters::processNFAs(size_t maxNFASize, Function<bool(NFA&&)>&& handler)
 {
 #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
     print();
@@ -487,9 +487,9 @@
             ASSERT(stack.last());
             generateNFAForSubtree(nfa, WTFMove(lastNode), *stack.last(), m_actions, maxNFASize);
         }
-        nfa.finalize();
 
-        handler(WTFMove(nfa));
+        if (!handler(WTFMove(nfa)))
+            return false;
 
         // Clean up any processed leaf nodes.
         while (true) {
@@ -503,6 +503,7 @@
                 break; // Leave the empty root.
         }
     }
+    return true;
 }
 
 } // namespace ContentExtensions

Modified: trunk/Source/WebCore/contentextensions/CombinedURLFilters.h (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/CombinedURLFilters.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/CombinedURLFilters.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -47,7 +47,7 @@
     void addPattern(uint64_t actionId, const Vector<Term>& pattern);
     void addDomain(uint64_t actionId, const String& domain);
 
-    void processNFAs(size_t maxNFASize, const WTF::Function<void(NFA&&)>& handler);
+    bool processNFAs(size_t maxNFASize, Function<bool(NFA&&)>&&);
     bool isEmpty() const;
 
 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp	2020-12-04 00:01:23 UTC (rev 270414)
@@ -212,7 +212,7 @@
 }
 
 template<typename Functor>
-static void compileToBytecode(CombinedURLFilters&& filters, UniversalActionSet&& universalActions, Functor writeBytecodeToClient)
+static bool compileToBytecode(CombinedURLFilters&& filters, UniversalActionSet&& universalActions, Functor writeBytecodeToClient)
 {
     // Smaller maxNFASizes risk high compiling and interpreting times from having too many DFAs,
     // larger maxNFASizes use too much memory when compiling.
@@ -243,22 +243,27 @@
 
     const unsigned smallDFASize = 100;
     DFACombiner smallDFACombiner;
-    filters.processNFAs(maxNFASize, [&](NFA&& nfa) {
+    bool processedSuccessfully = filters.processNFAs(maxNFASize, [&](NFA&& nfa) {
 #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
         dataLogF("NFA\n");
         nfa.debugPrintDot();
 #endif
         LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
-        DFA dfa = NFAToDFA::convert(nfa);
-        LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+        auto dfa = NFAToDFA::convert(WTFMove(nfa));
+        if (!dfa)
+            return false;
+        LOG_LARGE_STRUCTURES(*dfa, dfa->memoryUsed());
 
-        if (dfa.graphSize() < smallDFASize)
-            smallDFACombiner.addDFA(WTFMove(dfa));
+        if (dfa->graphSize() < smallDFASize)
+            smallDFACombiner.addDFA(WTFMove(*dfa));
         else {
-            dfa.minimize();
-            lowerDFAToBytecode(WTFMove(dfa));
+            dfa->minimize();
+            lowerDFAToBytecode(WTFMove(*dfa));
         }
+        return true;
     });
+    if (!processedSuccessfully)
+        return false;
 
     smallDFACombiner.combineDFAs(smallDFASize, [&](DFA&& dfa) {
         LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
@@ -281,6 +286,7 @@
         writeBytecodeToClient(WTFMove(bytecode));
     }
     LOG_LARGE_STRUCTURES(universalActions, universalActions.capacity() * sizeof(unsigned));
+    return true;
 }
 
 std::error_code compileRuleList(ContentExtensionCompilationClient& client, String&& ruleJSON, Vector<ContentExtensionRule>&& parsedRuleList)
@@ -414,16 +420,22 @@
     MonotonicTime totalNFAToByteCodeBuildTimeStart = MonotonicTime::now();
 #endif
 
-    compileToBytecode(WTFMove(filtersWithoutConditions), WTFMove(universalActionsWithoutConditions), [&](Vector<DFABytecode>&& bytecode) {
+    bool success = compileToBytecode(WTFMove(filtersWithoutConditions), WTFMove(universalActionsWithoutConditions), [&](Vector<DFABytecode>&& bytecode) {
         client.writeFiltersWithoutConditionsBytecode(WTFMove(bytecode));
     });
-    compileToBytecode(WTFMove(filtersWithConditions), WTFMove(universalActionsWithConditions), [&](Vector<DFABytecode>&& bytecode) {
+    if (!success)
+        return ContentExtensionError::ErrorWritingSerializedNFA;
+    success = compileToBytecode(WTFMove(filtersWithConditions), WTFMove(universalActionsWithConditions), [&](Vector<DFABytecode>&& bytecode) {
         client.writeFiltersWithConditionsBytecode(WTFMove(bytecode));
     });
-    compileToBytecode(WTFMove(topURLFilters), WTFMove(universalTopURLActions), [&](Vector<DFABytecode>&& bytecode) {
+    if (!success)
+        return ContentExtensionError::ErrorWritingSerializedNFA;
+    success = compileToBytecode(WTFMove(topURLFilters), WTFMove(universalTopURLActions), [&](Vector<DFABytecode>&& bytecode) {
         client.writeTopURLFiltersBytecode(WTFMove(bytecode));
     });
-    
+    if (!success)
+        return ContentExtensionError::ErrorWritingSerializedNFA;
+
 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
     MonotonicTime totalNFAToByteCodeBuildTimeEnd = MonotonicTime::now();
     dataLogF("    Time spent building and compiling the DFAs: %f\n", (totalNFAToByteCodeBuildTimeEnd - totalNFAToByteCodeBuildTimeStart).seconds());

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp	2020-12-04 00:01:23 UTC (rev 270414)
@@ -89,6 +89,8 @@
                 return "A list cannot have if-domain and unless-domain mixed with if-top-url and unless-top-url";
             case ContentExtensionError::JSONInvalidNotification:
                 return "A notify action must have a string notification";
+            case ContentExtensionError::ErrorWritingSerializedNFA:
+                return "Internal I/O error";
             }
 
             return std::string();

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionError.h (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/ContentExtensionError.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionError.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -59,6 +59,8 @@
     JSONInvalidCSSDisplayNoneActionType,
     JSONInvalidNotification,
     JSONInvalidRegex,
+
+    ErrorWritingSerializedNFA,
 };
 
 extern const char* WebKitContentBlockerDomain;

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionsDebugging.h (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/ContentExtensionsDebugging.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionsDebugging.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -33,9 +33,6 @@
 
 #define CONTENT_EXTENSIONS_PERFORMANCE_REPORTING 0
 
-#define CONTENT_EXTENSIONS_MEMORY_REPORTING 0
-#define CONTENT_EXTENSIONS_PAGE_SIZE 16384
-
 #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
 typedef WTF::CrashOnOverflow ContentExtensionsOverflowHandler;
 #else

Modified: trunk/Source/WebCore/contentextensions/DFA.cpp (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/DFA.cpp	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/DFA.cpp	2020-12-04 00:01:23 UTC (rev 270414)
@@ -51,14 +51,6 @@
         + nodes.capacity() * sizeof(DFANode);
 }
 
-void DFA::shrinkToFit()
-{
-    nodes.shrinkToFit();
-    actions.shrinkToFit();
-    transitionRanges.shrinkToFit();
-    transitionDestinations.shrinkToFit();
-}
-
 void DFA::minimize()
 {
     DFAMinimizer::minimize(*this);

Modified: trunk/Source/WebCore/contentextensions/DFA.h (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/DFA.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/DFA.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -39,7 +39,6 @@
 struct WEBCORE_EXPORT DFA {
     static DFA empty();
 
-    void shrinkToFit();
     void minimize();
     unsigned graphSize() const;
     size_t memoryUsed() const;

Modified: trunk/Source/WebCore/contentextensions/ImmutableNFA.h (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/ImmutableNFA.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/ImmutableNFA.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -58,114 +58,15 @@
     Vector<uint32_t, 0, ContentExtensionsOverflowHandler> epsilonTransitionsTargets;
     Vector<ActionType, 0, ContentExtensionsOverflowHandler> actions;
 
-    struct ConstTargetIterator {
-        const ImmutableNFA& immutableNFA;
-        uint32_t position;
-
-        const uint32_t& operator*() const { return immutableNFA.targets[position]; }
-        const uint32_t* operator->() const { return &immutableNFA.targets[position]; }
-
-        bool operator==(const ConstTargetIterator& other) const
-        {
-            ASSERT(&immutableNFA == &other.immutableNFA);
-            return position == other.position;
-        }
-        bool operator!=(const ConstTargetIterator& other) const { return !(*this == other); }
-
-        ConstTargetIterator& operator++()
-        {
-            ++position;
-            return *this;
-        }
-    };
-
-    struct IterableConstTargets {
-        const ImmutableNFA& immutableNFA;
-        uint32_t targetStart;
-        uint32_t targetEnd;
-
-        ConstTargetIterator begin() const { return { immutableNFA, targetStart }; }
-        ConstTargetIterator end() const { return { immutableNFA, targetEnd }; }
-    };
-
-    struct ConstRangeIterator {
-        const ImmutableNFA& immutableNFA;
-        uint32_t position;
-
-        bool operator==(const ConstRangeIterator& other) const
-        {
-            ASSERT(&immutableNFA == &other.immutableNFA);
-            return position == other.position;
-        }
-        bool operator!=(const ConstRangeIterator& other) const { return !(*this == other); }
-
-        ConstRangeIterator& operator++()
-        {
-            ++position;
-            return *this;
-        }
-
-        CharacterType first() const
-        {
-            return range().first;
-        }
-
-        CharacterType last() const
-        {
-            return range().last;
-        }
-
-        IterableConstTargets data() const
-        {
-            const ImmutableRange<CharacterType>& range = this->range();
-            return { immutableNFA, range.targetStart, range.targetEnd };
-        };
-
-    private:
-        const ImmutableRange<CharacterType>& range() const
-        {
-            return immutableNFA.transitions[position];
-        }
-    };
-
-    struct IterableConstRange {
-        const ImmutableNFA& immutableNFA;
-        uint32_t rangesStart;
-        uint32_t rangesEnd;
-
-        ConstRangeIterator begin() const { return { immutableNFA, rangesStart }; }
-        ConstRangeIterator end() const { return { immutableNFA, rangesEnd }; }
-
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
-        void debugPrint() const
-        {
-            for (const auto& range : *this)
-                WTFLogAlways("    %d-%d", range.first, range.last);
-        }
-#endif
-    };
-
-    IterableConstRange transitionsForNode(uint32_t nodeId) const
+    void clear()
     {
-        const ImmutableNFANode& node = nodes[nodeId];
-        return { *this, node.rangesStart, node.rangesEnd };
-    };
-
-    uint32_t root() const
-    {
-        RELEASE_ASSERT(!nodes.isEmpty());
-        return 0;
+        nodes.clear();
+        transitions.clear();
+        targets.clear();
+        epsilonTransitionsTargets.clear();
+        actions.clear();
     }
 
-    void finalize()
-    {
-        nodes.shrinkToFit();
-        transitions.shrinkToFit();
-        targets.shrinkToFit();
-        epsilonTransitionsTargets.shrinkToFit();
-        actions.shrinkToFit();
-    }
-
     size_t memoryUsed() const
     {
         return nodes.capacity() * sizeof(ImmutableNFANode)

Modified: trunk/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -35,9 +35,9 @@
 
 namespace ContentExtensions {
 
-// A ImmutableNFANodeBuilder let you build a NFA node by adding states and linking with other nodes.
-// Whe a builder is destructed, all its properties are finalized into the NFA. Using the NA with a live
-// builder results in undefined behaviors.
+// A ImmutableNFANodeBuilder let you build an NFA node by adding states and linking with other nodes.
+// When a builder is destructed, all its properties are finalized into the NFA. Using the NFA with a live
+// builder results in undefined behavior.
 template <typename CharacterType, typename ActionType>
 class ImmutableNFANodeBuilder {
     typedef ImmutableNFA<CharacterType, ActionType> TypedImmutableNFA;

Modified: trunk/Source/WebCore/contentextensions/NFAToDFA.cpp (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/NFAToDFA.cpp	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/NFAToDFA.cpp	2020-12-04 00:01:23 UTC (rev 270414)
@@ -33,6 +33,7 @@
 #include "ImmutableNFA.h"
 #include "MutableRangeList.h"
 #include "NFA.h"
+#include "SerializedNFA.h"
 #include <wtf/DataLog.h>
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
@@ -51,7 +52,7 @@
 // FIXME: include the hash inside NodeIdSet.
 typedef NFANodeIndexSet NodeIdSet;
 
-static inline void epsilonClosureExcludingSelf(NFA& nfa, unsigned nodeId, UniqueNodeList& output)
+static inline void epsilonClosureExcludingSelf(const SerializedNFA& nfa, unsigned nodeId, UniqueNodeList& output)
 {
     NodeIdSet closure({ nodeId });
     Vector<unsigned, 64, ContentExtensionsOverflowHandler> unprocessedNodes({ nodeId });
@@ -58,10 +59,10 @@
 
     do {
         unsigned unprocessedNodeId = unprocessedNodes.takeLast();
-        const auto& node = nfa.nodes[unprocessedNodeId];
+        const auto& node = nfa.nodes()[unprocessedNodeId];
 
         for (uint32_t epsilonTargetIndex = node.epsilonTransitionTargetsStart; epsilonTargetIndex < node.epsilonTransitionTargetsEnd; ++epsilonTargetIndex) {
-            uint32_t targetNodeId = nfa.epsilonTransitionsTargets[epsilonTargetIndex];
+            uint32_t targetNodeId = nfa.epsilonTransitionsTargets()[epsilonTargetIndex];
             auto addResult = closure.add(targetNodeId);
             if (addResult.isNewEntry) {
                 unprocessedNodes.append(targetNodeId);
@@ -73,18 +74,16 @@
     output.shrinkToFit();
 }
 
-static void resolveEpsilonClosures(NFA& nfa, NFANodeClosures& nfaNodeClosures)
+static NFANodeClosures resolveEpsilonClosures(const SerializedNFA& nfa)
 {
-    unsigned nfaGraphSize = nfa.nodes.size();
+    NFANodeClosures nfaNodeClosures;
+    unsigned nfaGraphSize = nfa.nodes().size();
     nfaNodeClosures.resize(nfaGraphSize);
 
     for (unsigned nodeId = 0; nodeId < nfaGraphSize; ++nodeId)
         epsilonClosureExcludingSelf(nfa, nodeId, nfaNodeClosures[nodeId]);
 
-    // Every nodes still point to that table, but we won't use it ever again.
-    // Clear it to get back the memory. That's not pretty but memory is important here, we have both
-    // graphs existing at the same time.
-    nfa.epsilonTransitionsTargets.clear();
+    return nfaNodeClosures;
 }
 
 static ALWAYS_INLINE void extendSetWithClosure(const NFANodeClosures& nfaNodeClosures, unsigned nodeId, NodeIdSet& set)
@@ -215,7 +214,7 @@
 typedef HashSet<std::unique_ptr<UniqueNodeIdSet>, UniqueNodeIdSetHash, UniqueNodeIdSetHashHashTraits> UniqueNodeIdSetTable;
 
 struct NodeIdSetToUniqueNodeIdSetSource {
-    NodeIdSetToUniqueNodeIdSetSource(DFA& dfa, const NFA& nfa, const NodeIdSet& nodeIdSet)
+    NodeIdSetToUniqueNodeIdSetSource(DFA& dfa, const SerializedNFA& nfa, const NodeIdSet& nodeIdSet)
         : dfa(dfa)
         , nfa(nfa)
         , nodeIdSet(nodeIdSet)
@@ -227,7 +226,7 @@
         this->hash = DefaultHash<unsigned>::hash(hash);
     }
     DFA& dfa;
-    const NFA& nfa;
+    const SerializedNFA& nfa;
     const NodeIdSet& nodeIdSet;
     unsigned hash;
 };
@@ -250,9 +249,9 @@
         HashSet<uint64_t, DefaultHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> actions;
 
         for (unsigned nfaNodeId : source.nodeIdSet) {
-            const auto& nfaNode = source.nfa.nodes[nfaNodeId];
+            const auto& nfaNode = source.nfa.nodes()[nfaNodeId];
             for (unsigned actionIndex = nfaNode.actionStart; actionIndex < nfaNode.actionEnd; ++actionIndex)
-                actions.add(source.nfa.actions[actionIndex]);
+                actions.add(source.nfa.actions()[actionIndex]);
         }
 
         unsigned actionsStart = source.dfa.actions.size();
@@ -299,7 +298,7 @@
     }
 };
 
-static inline void createCombinedTransition(PreallocatedNFANodeRangeList& combinedRangeList, const UniqueNodeIdSetImpl& sourceNodeSet, const NFA& immutableNFA, const NFANodeClosures& nfaNodeclosures)
+static inline void createCombinedTransition(PreallocatedNFANodeRangeList& combinedRangeList, const UniqueNodeIdSetImpl& sourceNodeSet, const SerializedNFA& serializedNFA, const NFANodeClosures& nfaNodeclosures)
 {
     combinedRangeList.clear();
 
@@ -308,14 +307,14 @@
     DataConverterWithEpsilonClosure converter { nfaNodeclosures };
     for (unsigned i = 0; i < sourceNodeSet.m_size; ++i) {
         unsigned nodeId = buffer[i];
-        auto transitions = immutableNFA.transitionsForNode(nodeId);
+        auto transitions = serializedNFA.transitionsForNode(nodeId);
         combinedRangeList.extend(transitions.begin(), transitions.end(), converter);
     }
 }
 
-static ALWAYS_INLINE unsigned getOrCreateDFANode(const NodeIdSet& nfaNodeSet, const NFA& nfa, DFA& dfa, UniqueNodeIdSetTable& uniqueNodeIdSetTable, UniqueNodeQueue& unprocessedNodes)
+static ALWAYS_INLINE unsigned getOrCreateDFANode(const NodeIdSet& nfaNodeSet, const SerializedNFA& serializedNFA, DFA& dfa, UniqueNodeIdSetTable& uniqueNodeIdSetTable, UniqueNodeQueue& unprocessedNodes)
 {
-    NodeIdSetToUniqueNodeIdSetSource nodeIdSetToUniqueNodeIdSetSource(dfa, nfa, nfaNodeSet);
+    NodeIdSetToUniqueNodeIdSetSource nodeIdSetToUniqueNodeIdSetSource(dfa, serializedNFA, nfaNodeSet);
     auto uniqueNodeIdAddResult = uniqueNodeIdSetTable.add<NodeIdSetToUniqueNodeIdSetTranslator>(nodeIdSetToUniqueNodeIdSetSource);
     if (uniqueNodeIdAddResult.isNewEntry)
         unprocessedNodes.append(uniqueNodeIdAddResult.iterator->impl());
@@ -323,19 +322,21 @@
     return uniqueNodeIdAddResult.iterator->impl()->m_dfaNodeId;
 }
 
-DFA NFAToDFA::convert(NFA& nfa)
+Optional<DFA> NFAToDFA::convert(NFA&& nfa)
 {
-    NFANodeClosures nfaNodeClosures;
-    resolveEpsilonClosures(nfa, nfaNodeClosures);
+    auto serializedNFA = SerializedNFA::serialize(WTFMove(nfa));
+    if (!serializedNFA)
+        return WTF::nullopt;
 
+    NFANodeClosures nfaNodeClosures = resolveEpsilonClosures(*serializedNFA);
     DFA dfa;
 
-    NodeIdSet initialSet({ nfa.root() });
-    extendSetWithClosure(nfaNodeClosures, nfa.root(), initialSet);
+    NodeIdSet initialSet({ serializedNFA->root() });
+    extendSetWithClosure(nfaNodeClosures, serializedNFA->root(), initialSet);
 
     UniqueNodeIdSetTable uniqueNodeIdSetTable;
 
-    NodeIdSetToUniqueNodeIdSetSource initialNodeIdSetToUniqueNodeIdSetSource(dfa, nfa, initialSet);
+    NodeIdSetToUniqueNodeIdSetSource initialNodeIdSetToUniqueNodeIdSetSource(dfa, *serializedNFA, initialSet);
     auto addResult = uniqueNodeIdSetTable.add<NodeIdSetToUniqueNodeIdSetTranslator>(initialNodeIdSetToUniqueNodeIdSetSource);
 
     UniqueNodeQueue unprocessedNodes;
@@ -344,11 +345,11 @@
     PreallocatedNFANodeRangeList combinedRangeList;
     do {
         UniqueNodeIdSetImpl* uniqueNodeIdSetImpl = unprocessedNodes.takeLast();
-        createCombinedTransition(combinedRangeList, *uniqueNodeIdSetImpl, nfa, nfaNodeClosures);
+        createCombinedTransition(combinedRangeList, *uniqueNodeIdSetImpl, *serializedNFA, nfaNodeClosures);
 
         unsigned transitionsStart = dfa.transitionRanges.size();
         for (const NFANodeRange& range : combinedRangeList) {
-            unsigned targetNodeId = getOrCreateDFANode(range.data, nfa, dfa, uniqueNodeIdSetTable, unprocessedNodes);
+            unsigned targetNodeId = getOrCreateDFANode(range.data, *serializedNFA, dfa, uniqueNodeIdSetTable, unprocessedNodes);
             dfa.transitionRanges.append({ range.first, range.last });
             dfa.transitionDestinations.append(targetNodeId);
         }
@@ -360,8 +361,7 @@
         dfaSourceNode.setTransitions(transitionsStart, static_cast<uint8_t>(transitionsLength));
     } while (!unprocessedNodes.isEmpty());
 
-    dfa.shrinkToFit();
-    return dfa;
+    return WTFMove(dfa);
 }
 
 } // namespace ContentExtensions

Modified: trunk/Source/WebCore/contentextensions/NFAToDFA.h (270413 => 270414)


--- trunk/Source/WebCore/contentextensions/NFAToDFA.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebCore/contentextensions/NFAToDFA.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -38,7 +38,7 @@
 // NFAToDFA provides a way to build a DFA corresponding to a NFA.
 class NFAToDFA {
 public:
-    WEBCORE_EXPORT static DFA convert(NFA&);
+    WEBCORE_EXPORT static Optional<DFA> convert(NFA&&);
 };
 
 } // namespace ContentExtensions

Added: trunk/Source/WebCore/contentextensions/SerializedNFA.cpp (0 => 270414)


--- trunk/Source/WebCore/contentextensions/SerializedNFA.cpp	                        (rev 0)
+++ trunk/Source/WebCore/contentextensions/SerializedNFA.cpp	2020-12-04 00:01:23 UTC (rev 270414)
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 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 "SerializedNFA.h"
+
+#if ENABLE(CONTENT_EXTENSIONS)
+
+namespace WebCore {
+namespace ContentExtensions {
+
+template<typename T>
+bool writeAllToFile(FileSystem::PlatformFileHandle file, const T& container)
+{
+    const char* bytes = reinterpret_cast<const char*>(container.data());
+    size_t bytesLength = container.size() * sizeof(container[0]);
+    const char* end = bytes + bytesLength;
+    while (bytes < end) {
+        auto written = FileSystem::writeToFile(file, bytes, bytesLength);
+        if (written == -1)
+            return false;
+        bytes += written;
+        bytesLength -= written;
+    }
+    return true;
+}
+
+Optional<SerializedNFA> SerializedNFA::serialize(NFA&& nfa)
+{
+    auto file = FileSystem::invalidPlatformFileHandle;
+    auto filename = FileSystem::openTemporaryFile("SerializedNFA", file);
+    if (!FileSystem::isHandleValid(file))
+        return WTF::nullopt;
+
+    bool wroteSuccessfully = writeAllToFile(file, nfa.nodes)
+        && writeAllToFile(file, nfa.transitions)
+        && writeAllToFile(file, nfa.targets)
+        && writeAllToFile(file, nfa.epsilonTransitionsTargets)
+        && writeAllToFile(file, nfa.actions);
+    if (!wroteSuccessfully) {
+        FileSystem::closeFile(file);
+        FileSystem::deleteFile(filename);
+        return WTF::nullopt;
+    }
+
+    bool mappedSuccessfully = false;
+    FileSystem::MappedFileData mappedFile(file, FileSystem::MappedFileMode::Private, mappedSuccessfully);
+    FileSystem::closeFile(file);
+    FileSystem::deleteFile(filename);
+    if (!mappedSuccessfully)
+        return WTF::nullopt;
+
+    Metadata metadata {
+        nfa.nodes.size(),
+        nfa.transitions.size(),
+        nfa.targets.size(),
+        nfa.epsilonTransitionsTargets.size(),
+        nfa.actions.size(),
+        0,
+        nfa.nodes.size() * sizeof(nfa.nodes[0]),
+        nfa.nodes.size() * sizeof(nfa.nodes[0])
+            + nfa.transitions.size() * sizeof(nfa.transitions[0]),
+        nfa.nodes.size() * sizeof(nfa.nodes[0])
+            + nfa.transitions.size() * sizeof(nfa.transitions[0])
+            + nfa.targets.size() * sizeof(nfa.targets[0]),
+        nfa.nodes.size() * sizeof(nfa.nodes[0])
+            + nfa.transitions.size() * sizeof(nfa.transitions[0])
+            + nfa.targets.size() * sizeof(nfa.targets[0])
+            + nfa.epsilonTransitionsTargets.size() * sizeof(nfa.epsilonTransitionsTargets[0])
+    };
+
+    nfa.clear();
+
+    return {{ WTFMove(mappedFile), WTFMove(metadata) }};
+}
+
+SerializedNFA::SerializedNFA(FileSystem::MappedFileData&& file, Metadata&& metadata)
+    : m_file(WTFMove(file))
+    , m_metadata(WTFMove(metadata))
+{
+}
+
+template<typename T>
+const T* SerializedNFA::pointerAtOffsetInFile(size_t offset) const
+{
+    return reinterpret_cast<const T*>(reinterpret_cast<const uint8_t*>(m_file.data()) + offset);
+}
+
+auto SerializedNFA::nodes() const -> const Range<ImmutableNFANode>
+{
+    return { pointerAtOffsetInFile<ImmutableNFANode>(m_metadata.nodesOffset), m_metadata.nodesSize };
+}
+
+auto SerializedNFA::transitions() const -> const Range<ImmutableRange<char>>
+{
+    return { pointerAtOffsetInFile<ImmutableRange<char>>(m_metadata.transitionsOffset), m_metadata.transitionsSize };
+}
+
+auto SerializedNFA::targets() const -> const Range<uint32_t>
+{
+    return { pointerAtOffsetInFile<uint32_t>(m_metadata.targetsOffset), m_metadata.targetsSize };
+}
+
+auto SerializedNFA::epsilonTransitionsTargets() const -> const Range<uint32_t>
+{
+    return { pointerAtOffsetInFile<uint32_t>(m_metadata.epsilonTransitionsTargetsOffset), m_metadata.epsilonTransitionsTargetsSize };
+}
+
+auto SerializedNFA::actions() const -> const Range<uint64_t>
+{
+    return { pointerAtOffsetInFile<uint64_t>(m_metadata.actionsOffset), m_metadata.actionsSize };
+}
+
+} // namespace ContentExtensions
+} // namespace WebCore
+
+#endif // ENABLE(CONTENT_EXTENSIONS)

Copied: trunk/Source/WebCore/contentextensions/SerializedNFA.h (from rev 270413, trunk/Source/WebCore/contentextensions/ImmutableNFA.h) (0 => 270414)


--- trunk/Source/WebCore/contentextensions/SerializedNFA.h	                        (rev 0)
+++ trunk/Source/WebCore/contentextensions/SerializedNFA.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#if ENABLE(CONTENT_EXTENSIONS)
+
+#include <wtf/FileSystem.h>
+
+namespace WebCore {
+namespace ContentExtensions {
+
+struct NFA;
+
+class SerializedNFA {
+public:
+    static Optional<SerializedNFA> serialize(NFA&&);
+    SerializedNFA(SerializedNFA&&) = default;
+
+    template<typename T>
+    class Range {
+    public:
+        Range(const T* begin, size_t size)
+            : m_begin(begin)
+            , m_size(size) { }
+        const T* begin() const { return m_begin; }
+        const T* end() const { return m_begin + m_size; }
+        size_t size() const { return m_size; }
+        const T& operator[](size_t i) const
+        {
+            RELEASE_ASSERT(i < m_size);
+            return begin()[i];
+        }
+    private:
+        const T* m_begin { nullptr };
+        size_t m_size { 0 };
+    };
+
+    const Range<ImmutableNFANode> nodes() const;
+    const Range<ImmutableRange<char>> transitions() const;
+    const Range<uint32_t> targets() const;
+    const Range<uint32_t> epsilonTransitionsTargets() const;
+    const Range<uint64_t> actions() const;
+
+    uint32_t root() const
+    {
+        RELEASE_ASSERT(nodes().size());
+        return 0;
+    }
+
+    struct ConstTargetIterator {
+        const SerializedNFA& serializedNFA;
+        uint32_t position;
+
+        const uint32_t& operator*() const { return serializedNFA.targets()[position]; }
+        const uint32_t* operator->() const { return &serializedNFA.targets()[position]; }
+
+        bool operator==(const ConstTargetIterator& other) const
+        {
+            ASSERT(&serializedNFA == &other.serializedNFA);
+            return position == other.position;
+        }
+        bool operator!=(const ConstTargetIterator& other) const { return !(*this == other); }
+
+        ConstTargetIterator& operator++()
+        {
+            ++position;
+            return *this;
+        }
+    };
+
+    struct IterableConstTargets {
+        const SerializedNFA& serializedNFA;
+        uint32_t targetStart;
+        uint32_t targetEnd;
+
+        ConstTargetIterator begin() const { return { serializedNFA, targetStart }; }
+        ConstTargetIterator end() const { return { serializedNFA, targetEnd }; }
+    };
+
+    struct ConstRangeIterator {
+        const SerializedNFA& serializedNFA;
+        uint32_t position;
+
+        bool operator==(const ConstRangeIterator& other) const
+        {
+            ASSERT(&serializedNFA == &other.serializedNFA);
+            return position == other.position;
+        }
+        bool operator!=(const ConstRangeIterator& other) const { return !(*this == other); }
+
+        ConstRangeIterator& operator++()
+        {
+            ++position;
+            return *this;
+        }
+
+        char first() const
+        {
+            return range().first;
+        }
+
+        char last() const
+        {
+            return range().last;
+        }
+
+        IterableConstTargets data() const
+        {
+            const ImmutableRange<char>& range = this->range();
+            return { serializedNFA, range.targetStart, range.targetEnd };
+        };
+
+    private:
+        const ImmutableRange<char>& range() const
+        {
+            return serializedNFA.transitions()[position];
+        }
+    };
+
+    struct IterableConstRange {
+        const SerializedNFA& serializedNFA;
+        uint32_t rangesStart;
+        uint32_t rangesEnd;
+
+        ConstRangeIterator begin() const { return { serializedNFA, rangesStart }; }
+        ConstRangeIterator end() const { return { serializedNFA, rangesEnd }; }
+
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+        void debugPrint() const
+        {
+            for (const auto& range : *this)
+                WTFLogAlways("    %d-%d", range.first, range.last);
+        }
+#endif
+    };
+
+    IterableConstRange transitionsForNode(uint32_t nodeId) const
+    {
+        const auto& node = nodes()[nodeId];
+        return { *this, node.rangesStart, node.rangesEnd };
+    }
+
+private:
+    struct Metadata {
+        size_t nodesSize { 0 };
+        size_t transitionsSize { 0 };
+        size_t targetsSize { 0 };
+        size_t epsilonTransitionsTargetsSize { 0 };
+        size_t actionsSize { 0 };
+
+        size_t nodesOffset { 0 };
+        size_t transitionsOffset { 0 };
+        size_t targetsOffset { 0 };
+        size_t epsilonTransitionsTargetsOffset { 0 };
+        size_t actionsOffset { 0 };
+    };
+    SerializedNFA(FileSystem::MappedFileData&&, Metadata&&);
+
+    template<typename T>
+    const T* pointerAtOffsetInFile(size_t) const;
+    
+    FileSystem::MappedFileData m_file;
+    Metadata m_metadata;
+};
+
+} // namespace ContentExtensions
+} // namespace WebCore
+
+#endif // ENABLE(CONTENT_EXTENSIONS)

Modified: trunk/Source/WebKit/ChangeLog (270413 => 270414)


--- trunk/Source/WebKit/ChangeLog	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebKit/ChangeLog	2020-12-04 00:01:23 UTC (rev 270414)
@@ -1,3 +1,15 @@
+2020-12-03  Alex Christensen  <[email protected]>
+
+        Serialize NFA to disk before converting it to a DFA when compiling a WKContentRuleList
+        https://bugs.webkit.org/show_bug.cgi?id=219452
+
+        Reviewed by Geoffrey Garen.
+
+        * UIProcess/API/Cocoa/WKContentRuleListStore.mm:
+        (-[WKContentRuleListStore _compileContentRuleListForIdentifier:encodedContentRuleList:completionHandler:]):
+        * UIProcess/API/Cocoa/WKContentRuleListStorePrivate.h:
+        Remove NS_RELEASES_ARGUMENT because it was incorrect and unnecessary because the WTF::String is copied to a background thread.
+
 2020-12-03  Chris Dumez  <[email protected]>
 
         Make sure the GPUConnectionToWebProcess gets destroyed when the connection to the WebProcess gets severed

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKContentRuleListStore.mm (270413 => 270414)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKContentRuleListStore.mm	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKContentRuleListStore.mm	2020-12-04 00:01:23 UTC (rev 270414)
@@ -146,8 +146,7 @@
     });
 }
 
-// NS_RELEASES_ARGUMENT to keep peak memory usage low.
-- (void)_compileContentRuleListForIdentifier:(NSString *)identifier encodedContentRuleList:(NSString *) NS_RELEASES_ARGUMENT encodedContentRuleList completionHandler:(void (^)(WKContentRuleList *, NSError *))completionHandler
+- (void)_compileContentRuleListForIdentifier:(NSString *)identifier encodedContentRuleList:(NSString *) encodedContentRuleList completionHandler:(void (^)(WKContentRuleList *, NSError *))completionHandler
 {
     String json(encodedContentRuleList);
     [encodedContentRuleList release];

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKContentRuleListStorePrivate.h (270413 => 270414)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKContentRuleListStorePrivate.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKContentRuleListStorePrivate.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -32,8 +32,7 @@
 - (void)_invalidateContentRuleListVersionForIdentifier:(NSString *)identifier;
 - (void)_getContentRuleListSourceForIdentifier:(NSString *)identifier completionHandler:(void (^)(NSString*))completionHandler;
 
-// NS_RELEASES_ARGUMENT to keep peak memory usage low.
-- (void)_compileContentRuleListForIdentifier:(NSString *)identifier encodedContentRuleList:(NSString *) NS_RELEASES_ARGUMENT encodedContentRuleList completionHandler:(void (^)(WKContentRuleList *, NSError *))completionHandler;
+- (void)_compileContentRuleListForIdentifier:(NSString *)identifier encodedContentRuleList:(NSString *) encodedContentRuleList completionHandler:(void (^)(WKContentRuleList *, NSError *))completionHandler;
 
 // To maintain compatibility with _WKUserContentExtensionStore
 // FIXME: Add something to existing clients of _WKUserContentExtensionStore to migrate files from legacy filenames,

Modified: trunk/Tools/ChangeLog (270413 => 270414)


--- trunk/Tools/ChangeLog	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Tools/ChangeLog	2020-12-04 00:01:23 UTC (rev 270414)
@@ -1,3 +1,19 @@
+2020-12-03  Alex Christensen  <[email protected]>
+
+        Serialize NFA to disk before converting it to a DFA when compiling a WKContentRuleList
+        https://bugs.webkit.org/show_bug.cgi?id=219452
+
+        Reviewed by Geoffrey Garen.
+
+        Update syntax of existing tests, which cover behavior quite well.
+
+        * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+        (TestWebKitAPI::createNFAs):
+        (TestWebKitAPI::TEST_F):
+        * TestWebKitAPI/Tests/WebCore/DFAHelpers.h:
+        (TestWebKitAPI::createNFAs):
+        (TestWebKitAPI::buildDFAFromPatterns):
+
 2020-12-03  Jonathan Bedard  <[email protected]>
 
         [webkitscmpy] Incorrect identifier on remote SVN branches

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (270413 => 270414)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp	2020-12-04 00:01:23 UTC (rev 270414)
@@ -227,11 +227,10 @@
 static Vector<ContentExtensions::NFA> createNFAs(ContentExtensions::CombinedURLFilters& combinedURLFilters)
 {
     Vector<ContentExtensions::NFA> nfas;
-
     combinedURLFilters.processNFAs(std::numeric_limits<size_t>::max(), [&](ContentExtensions::NFA&& nfa) {
         nfas.append(WTFMove(nfa));
+        return true;
     });
-
     return nfas;
 }
 
@@ -576,7 +575,7 @@
     EXPECT_EQ(1ul, nfas.size());
     EXPECT_EQ(12ul, nfas.first().nodes.size());
 
-    ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first());
+    ContentExtensions::DFA dfa = *ContentExtensions::NFAToDFA::convert(WTFMove(nfas.first()));
     Vector<ContentExtensions::DFABytecode> bytecode;
     ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
     compiler.compile();
@@ -602,7 +601,7 @@
     EXPECT_EQ(1ul, nfas.size());
     EXPECT_EQ(17ul, nfas.first().nodes.size());
 
-    ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first());
+    ContentExtensions::DFA dfa = *ContentExtensions::NFAToDFA::convert(WTFMove(nfas.first()));
     Vector<ContentExtensions::DFABytecode> bytecode;
     ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
     compiler.compile();
@@ -1125,7 +1124,7 @@
     EXPECT_EQ(1ul, nfas.size());
     EXPECT_EQ(7ul, nfas.first().nodes.size());
 
-    ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first());
+    ContentExtensions::DFA dfa = *ContentExtensions::NFAToDFA::convert(WTFMove(nfas.first()));
     Vector<ContentExtensions::DFABytecode> bytecode;
     ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
     compiler.compile();
@@ -1271,12 +1270,13 @@
     Vector<ContentExtensions::NFA> nfas;
     combinedURLFilters.processNFAs(std::numeric_limits<size_t>::max(), [&](ContentExtensions::NFA&& nfa) {
         nfas.append(WTFMove(nfa));
+        return true;
     });
     EXPECT_EQ(nfas.size(), 1ull);
     
     Vector<ContentExtensions::DFA> dfas;
-    for (auto& nfa : nfas)
-        dfas.append(ContentExtensions::NFAToDFA::convert(nfa));
+    for (auto&& nfa : WTFMove(nfas))
+        dfas.append(*ContentExtensions::NFAToDFA::convert(WTFMove(nfa)));
     EXPECT_EQ(dfas.size(), 1ull);
     
     Vector<ContentExtensions::DFABytecode> combinedBytecode;
@@ -1685,12 +1685,13 @@
         Vector<ContentExtensions::NFA> nfas;
         combinedURLFilters.processNFAs(i, [&](ContentExtensions::NFA&& nfa) {
             nfas.append(WTFMove(nfa));
+            return true;
         });
         EXPECT_EQ(nfas.size(), expectedNFACounts[i]);
         
         Vector<ContentExtensions::DFA> dfas;
-        for (auto& nfa : nfas)
-            dfas.append(ContentExtensions::NFAToDFA::convert(nfa));
+        for (auto& nfa : WTFMove(nfas))
+            dfas.append(*ContentExtensions::NFAToDFA::convert(WTFMove(nfa)));
         
         Vector<ContentExtensions::DFABytecode> combinedBytecode;
         for (const auto& dfa : dfas) {

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h (270413 => 270414)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h	2020-12-03 23:54:52 UTC (rev 270413)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h	2020-12-04 00:01:23 UTC (rev 270414)
@@ -47,11 +47,10 @@
 static Vector<ContentExtensions::NFA> createNFAs(ContentExtensions::CombinedURLFilters& combinedURLFilters)
 {
     Vector<ContentExtensions::NFA> nfas;
-
     combinedURLFilters.processNFAs(std::numeric_limits<size_t>::max(), [&](ContentExtensions::NFA&& nfa) {
         nfas.append(WTFMove(nfa));
+        return true;
     });
-
     return nfas;
 }
 
@@ -63,7 +62,8 @@
     for (const char* pattern : patterns)
         parser.addPattern(pattern, false, 0);
     Vector<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters);
-    return ContentExtensions::NFAToDFA::convert(nfas[0]);
+    EXPECT_EQ(1ul, nfas.size());
+    return *ContentExtensions::NFAToDFA::convert(WTFMove(nfas.first()));
 }
 
 } // namespace TestWebKitAPI
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to