Title: [187087] branches/safari-601.1-branch

Diff

Modified: branches/safari-601.1-branch/Source/WTF/ChangeLog (187086 => 187087)


--- branches/safari-601.1-branch/Source/WTF/ChangeLog	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Source/WTF/ChangeLog	2015-07-21 05:08:21 UTC (rev 187087)
@@ -1,3 +1,22 @@
+2015-07-20  Matthew Hanson  <matthew_han...@apple.com>
+
+        Merge r186910. rdar://problem/21863296
+
+    2015-07-16  Benjamin Poulain  <bpoul...@apple.com>
+
+            [Content extensions] Combine suffixes when generating NFAs
+            https://bugs.webkit.org/show_bug.cgi?id=146961
+
+            Reviewed by Alex Christensen.
+
+            * wtf/Vector.h:
+            (WTF::minCapacity>::Vector):
+            (WTF::=):
+            Copying a vector with a different inline capacity was broken due to
+            the addition of MinimumCapacity.
+
+            This feature was needed by this patch so I fixed WTF.
+
 2015-07-15  Lucas Forschler  <lforsch...@apple.com>
 
         Merge r186826

Modified: branches/safari-601.1-branch/Source/WTF/wtf/Vector.h (187086 => 187087)


--- branches/safari-601.1-branch/Source/WTF/wtf/Vector.h	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Source/WTF/wtf/Vector.h	2015-07-21 05:08:21 UTC (rev 187087)
@@ -638,12 +638,12 @@
     }
 
     Vector(const Vector&);
-    template<size_t otherCapacity, typename otherOverflowBehaviour>
-    explicit Vector(const Vector<T, otherCapacity, otherOverflowBehaviour>&);
+    template<size_t otherCapacity, typename otherOverflowBehaviour, size_t otherMinimumCapacity>
+    explicit Vector(const Vector<T, otherCapacity, otherOverflowBehaviour, otherMinimumCapacity>&);
 
     Vector& operator=(const Vector&);
-    template<size_t otherCapacity, typename otherOverflowBehaviour>
-    Vector& operator=(const Vector<T, otherCapacity, otherOverflowBehaviour>&);
+    template<size_t otherCapacity, typename otherOverflowBehaviour, size_t otherMinimumCapacity>
+    Vector& operator=(const Vector<T, otherCapacity, otherOverflowBehaviour, otherMinimumCapacity>&);
 
     Vector(Vector&&);
     Vector& operator=(Vector&&);
@@ -819,8 +819,8 @@
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity>
-template<size_t otherCapacity, typename otherOverflowBehaviour>
-Vector<T, inlineCapacity, OverflowHandler, minCapacity>::Vector(const Vector<T, otherCapacity, otherOverflowBehaviour>& other)
+template<size_t otherCapacity, typename otherOverflowBehaviour, size_t otherMinimumCapacity>
+Vector<T, inlineCapacity, OverflowHandler, minCapacity>::Vector(const Vector<T, otherCapacity, otherOverflowBehaviour, otherMinimumCapacity>& other)
     : Base(other.capacity(), other.size())
 {
     asanSetInitialBufferSizeTo(other.size());
@@ -855,8 +855,8 @@
 inline bool typelessPointersAreEqual(const void* a, const void* b) { return a == b; }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity>
-template<size_t otherCapacity, typename otherOverflowBehaviour>
-Vector<T, inlineCapacity, OverflowHandler, minCapacity>& Vector<T, inlineCapacity, OverflowHandler, minCapacity>::operator=(const Vector<T, otherCapacity, otherOverflowBehaviour>& other)
+template<size_t otherCapacity, typename otherOverflowBehaviour, size_t otherMinimumCapacity>
+Vector<T, inlineCapacity, OverflowHandler, minCapacity>& Vector<T, inlineCapacity, OverflowHandler, minCapacity>::operator=(const Vector<T, otherCapacity, otherOverflowBehaviour, otherMinimumCapacity>& other)
 {
     // If the inline capacities match, we should call the more specific
     // template.  If the inline capacities don't match, the two objects

Modified: branches/safari-601.1-branch/Source/WebCore/ChangeLog (187086 => 187087)


--- branches/safari-601.1-branch/Source/WebCore/ChangeLog	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Source/WebCore/ChangeLog	2015-07-21 05:08:21 UTC (rev 187087)
@@ -1,5 +1,110 @@
 2015-07-20  Matthew Hanson  <matthew_han...@apple.com>
 
+        Merge r186910. rdar://problem/21863296
+
+    2015-07-16  Benjamin Poulain  <bpoul...@apple.com>
+
+            [Content extensions] Combine suffixes when generating NFAs
+            https://bugs.webkit.org/show_bug.cgi?id=146961
+
+            Reviewed by Alex Christensen.
+
+            In this patch, I add a mechanism very similar to the prefix tree
+            but for the suffix (called a reverse suffix tree here).
+
+            The idea is here is to reuse the existing NFA nodes when generating
+            a chain of suffix Term that were already generated previously.
+            When generating a disjunction ending with the same suffix, we now
+            have the same trailing NFA nodes for both sides of the disjunction.
+
+            Mixing the prefix and suffix generation can be tricky, we do not want
+            transitions from a pattern to creep into the suffix of an other.
+
+            To avoid any conflict, the rules here are very simple:
+            -Only use the reverse suffix tree for terms without actions
+             up to a leaf term with actions.
+
+             This rule ensure that no action will accidentally make its way
+             to an other rule by resuing a vertex of the reverse suffix tree.
+
+            -Only use the reverse suffix tree for chains of terms in which
+             each term only has zero or one following term.
+
+             With this condition, when taking any vertex of the reverse suffix
+             tree, there is only one edge that move out of that vertex when reading
+             from left to right.
+             For any vertex, there is only one possible string generated
+             left-to-right, a single suffix.
+
+            This is overly restrictive but it is fast, easier to verify, and it works
+            well in practice.
+            For all the more complicated cases, we can count on the Minimizer to
+            find a better solution.
+
+
+            With all the simple suffixes merged, our NFAs are smaller, which
+            let us combine more patterns.
+            The DFAs are also smaller and faster to produce since their size
+            is relative to the NFA sizes.
+
+            Overall, I get the following gains:
+            -Chris's test case:
+                compile time -40%.
+                bytecode size -14%.
+            -Armand's test case:
+                compile time -53%.
+                bytecode size -13%.
+
+            * WebCore.xcodeproj/project.pbxproj:
+            * contentextensions/CombinedURLFilters.cpp:
+            (WebCore::ContentExtensions::ActiveSubtree::ActiveSubtree):
+            (WebCore::ContentExtensions::generateInfixUnsuitableForReverseSuffixTree):
+            (WebCore::ContentExtensions::generateSuffixWithReverseSuffixTree):
+            (WebCore::ContentExtensions::clearReverseSuffixTree):
+            (WebCore::ContentExtensions::generateNFAForSubtree):
+            * contentextensions/DFA.cpp:
+            (WebCore::ContentExtensions::DFA::debugPrintDot):
+            Forgot to close a tag, dot was not happy.
+
+            * contentextensions/HashableActionList.h: Added.
+            (WebCore::ContentExtensions::HashableActionList::HashableActionList):
+            (WebCore::ContentExtensions::HashableActionList::isEmptyValue):
+            (WebCore::ContentExtensions::HashableActionList::isDeletedValue):
+            (WebCore::ContentExtensions::HashableActionList::operator==):
+            (WebCore::ContentExtensions::HashableActionList::operator!=):
+            (WebCore::ContentExtensions::HashableActionListHash::hash):
+            (WebCore::ContentExtensions::HashableActionListHash::equal):
+            We need a way to group reverse suffix tree by their terminal actions.
+            This new hash structure lets us find unique vertex for a list of actions
+            in any order.
+
+            * contentextensions/ImmutableNFANodeBuilder.h:
+            (WebCore::ContentExtensions::ImmutableNFANodeBuilder::isValid):
+            (WebCore::ContentExtensions::ImmutableNFANodeBuilder::nodeId):
+            (WebCore::ContentExtensions::ImmutableNFANodeBuilder::addTransition):
+            (WebCore::ContentExtensions::ImmutableNFANodeBuilder::addEpsilonTransition):
+            (WebCore::ContentExtensions::ImmutableNFANodeBuilder::ImmutableNFANodeBuilder): Deleted.
+            (WebCore::ContentExtensions::ImmutableNFANodeBuilder::~ImmutableNFANodeBuilder): Deleted.
+            (WebCore::ContentExtensions::ImmutableNFANodeBuilder::operator=): Deleted.
+            * contentextensions/Term.h:
+            (WebCore::ContentExtensions::Term::generateGraph):
+            (WebCore::ContentExtensions::Term::generateSubgraphForAtom):
+            Node building changes a bit.
+
+            Previously, it was assumed nodes are always built from left to right.
+            Getting the node on the right was done by providing the left node and the term
+            doing the transition.
+
+            Now we have both left to right and right to left generation.
+
+            The right-to-left has a specific property: no edge can be added after
+            it's initial term (rule 2 of our reverse suffix tree). This simplifies
+            things a bit since we can finalize all the nodes in the suffix tree.
+            All we need is to keep their ID to be able to link new nodes
+            to the reverse suffix tree.
+
+2015-07-20  Matthew Hanson  <matthew_han...@apple.com>
+
         Merge r186715. rdar://problem/21863296
 
     2015-07-11  Benjamin Poulain  <benja...@webkit.org>

Modified: branches/safari-601.1-branch/Source/WebCore/WebCore.xcodeproj/project.pbxproj (187086 => 187087)


--- branches/safari-601.1-branch/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2015-07-21 05:08:21 UTC (rev 187087)
@@ -1040,6 +1040,7 @@
 		26E944D91AC4B2DD007B85B5 /* CombinedURLFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 26E944D51AC4B2DD007B85B5 /* CombinedURLFilters.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		26E944DD1AC4B4EA007B85B5 /* Term.h in Headers */ = {isa = PBXBuildFile; fileRef = 26E944DC1AC4B4EA007B85B5 /* Term.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		26E98A10130A9FCA008EB7B2 /* TextCodecASCIIFastPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 26E98A0F130A9FCA008EB7B2 /* TextCodecASCIIFastPath.h */; };
+		26EA89A71B4F2B75008C5FD2 /* HashableActionList.h in Headers */ = {isa = PBXBuildFile; fileRef = 26EA89A61B4F2B75008C5FD2 /* HashableActionList.h */; };
 		26F0C8971A2E724B002794F8 /* ContentExtensionParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F0C8951A2E724B002794F8 /* ContentExtensionParser.cpp */; };
 		26F0C8981A2E724B002794F8 /* ContentExtensionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 26F0C8961A2E724B002794F8 /* ContentExtensionParser.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		26F0C89B1A2EC110002794F8 /* ContentExtensionRule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F0C8991A2EC110002794F8 /* ContentExtensionRule.cpp */; };
@@ -8194,6 +8195,7 @@
 		26E944D51AC4B2DD007B85B5 /* CombinedURLFilters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CombinedURLFilters.h; sourceTree = "<group>"; };
 		26E944DC1AC4B4EA007B85B5 /* Term.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Term.h; sourceTree = "<group>"; };
 		26E98A0F130A9FCA008EB7B2 /* TextCodecASCIIFastPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCodecASCIIFastPath.h; sourceTree = "<group>"; };
+		26EA89A61B4F2B75008C5FD2 /* HashableActionList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HashableActionList.h; sourceTree = "<group>"; };
 		26F0C8951A2E724B002794F8 /* ContentExtensionParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentExtensionParser.cpp; sourceTree = "<group>"; };
 		26F0C8961A2E724B002794F8 /* ContentExtensionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentExtensionParser.h; sourceTree = "<group>"; };
 		26F0C8991A2EC110002794F8 /* ContentExtensionRule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentExtensionRule.cpp; sourceTree = "<group>"; };
@@ -15724,6 +15726,7 @@
 				26A517FC1AB92238006335DF /* DFAMinimizer.h */,
 				26D4E8451B42539D00E033A2 /* DFANode.cpp */,
 				267725F91A5B3AD9003C24DD /* DFANode.h */,
+				26EA89A61B4F2B75008C5FD2 /* HashableActionList.h */,
 				26F756B21B3B66F70005DD79 /* ImmutableNFA.h */,
 				26F756B41B3B68F20005DD79 /* ImmutableNFANodeBuilder.h */,
 				26F756AE1B3B65AC0005DD79 /* MutableRange.h */,
@@ -27268,6 +27271,7 @@
 				0709D78F1AE55554004E42F8 /* WebMediaSessionManager.h in Headers */,
 				0709D7951AE55A29004E42F8 /* WebMediaSessionManagerClient.h in Headers */,
 				0709D7931AE5557E004E42F8 /* WebMediaSessionManagerMac.h in Headers */,
+				26EA89A71B4F2B75008C5FD2 /* HashableActionList.h in Headers */,
 				E1A3162D134BC32D007C9A4F /* WebNSAttributedStringExtras.h in Headers */,
 				99CC0B6B18BEA1FF006CEBCC /* WebReplayInputs.h in Headers */,
 				A502C5DF13049B3500FC7D53 /* WebSafeGCActivityCallbackIOS.h in Headers */,

Modified: branches/safari-601.1-branch/Source/WebCore/contentextensions/CombinedURLFilters.cpp (187086 => 187087)


--- branches/safari-601.1-branch/Source/WebCore/contentextensions/CombinedURLFilters.cpp	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Source/WebCore/contentextensions/CombinedURLFilters.cpp	2015-07-21 05:08:21 UTC (rev 187087)
@@ -28,6 +28,7 @@
 
 #if ENABLE(CONTENT_EXTENSIONS)
 
+#include "HashableActionList.h"
 #include "Term.h"
 #include <wtf/DataLog.h>
 #include <wtf/Vector.h>
@@ -48,6 +49,19 @@
     PrefixTreeEdges edges;
 };
 
+struct ReverseSuffixTreeVertex;
+struct ReverseSuffixTreeEdge {
+    const Term* term;
+    std::unique_ptr<ReverseSuffixTreeVertex> child;
+};
+typedef Vector<ReverseSuffixTreeEdge, 0, WTF::CrashOnOverflow, 1> ReverseSuffixTreeEdges;
+
+struct ReverseSuffixTreeVertex {
+    ReverseSuffixTreeEdges edges;
+    uint32_t nodeId;
+};
+typedef HashMap<HashableActionList, ReverseSuffixTreeVertex, HashableActionListHash, HashableActionListHashTraits> ReverseSuffixTreeRoots;
+
 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
 static size_t recursiveMemoryUsed(const PrefixTreeVertex& vertex)
 {
@@ -206,39 +220,186 @@
         actions.append(actionId);
 }
 
-static void generateNFAForSubtree(NFA& nfa, ImmutableCharNFANodeBuilder&& subtreeRoot, PrefixTreeVertex& root, HashMap<const PrefixTreeVertex*, ActionList>& actions, size_t maxNFASize)
+struct ActiveSubtree {
+    ActiveSubtree(PrefixTreeVertex& vertex, ImmutableCharNFANodeBuilder&& nfaNode, unsigned edgeIndex)
+        : vertex(vertex)
+        , nfaNode(WTF::move(nfaNode))
+        , edgeIndex(edgeIndex)
+    {
+    }
+    PrefixTreeVertex& vertex;
+    ImmutableCharNFANodeBuilder nfaNode;
+    unsigned edgeIndex;
+};
+
+static void generateInfixUnsuitableForReverseSuffixTree(NFA& nfa, Vector<ActiveSubtree>& stack, const HashMap<const PrefixTreeVertex*, ActionList>& actions)
 {
+    // To avoid conflicts, we use the reverse suffix tree for subtrees that do not merge
+    // in the prefix tree.
+    //
+    // We only unify the suffixes to the actions on the leaf.
+    // If there are actions inside the tree, we generate the part of the subtree up to the action.
+    //
+    // If we accidentally insert a node with action inside the reverse-suffix-tree, we would create
+    // new actions on unrelated pattern when unifying their suffixes.
+    for (unsigned i = stack.size() - 1; i--;) {
+        ActiveSubtree& activeSubtree = stack[i];
+        if (activeSubtree.nfaNode.isValid())
+            return;
+
+        RELEASE_ASSERT_WITH_MESSAGE(i > 0, "The bottom of the stack must be the root of our fixed-length subtree. It should have it the isValid() case above.");
+
+        auto actionsIterator = actions.find(&activeSubtree.vertex);
+        bool hasActionInsideTree = actionsIterator != actions.end();
+
+        // Stricto sensu, we should count the number of exit edges with fixed length.
+        // That is costly and unlikely to matter in practice.
+        bool hasSingleOutcome = activeSubtree.vertex.edges.size() == 1;
+
+        if (hasActionInsideTree || !hasSingleOutcome) {
+            // Go back to the end of the subtree that has already been generated.
+            // From there, generate everything up to the vertex we found.
+            unsigned end = i;
+            unsigned beginning = end;
+
+            ActiveSubtree* sourceActiveSubtree = nullptr;
+            while (beginning--) {
+                ActiveSubtree& activeSubtree = stack[beginning];
+                if (activeSubtree.nfaNode.isValid()) {
+                    sourceActiveSubtree = &activeSubtree;
+                    break;
+                }
+            }
+            ASSERT_WITH_MESSAGE(sourceActiveSubtree, "The root should always have a valid generator.");
+
+            for (unsigned stackIndex = beginning + 1; stackIndex <= end; ++stackIndex) {
+                ImmutableCharNFANodeBuilder& sourceNode = sourceActiveSubtree->nfaNode;
+                ASSERT(sourceNode.isValid());
+                auto& edge = sourceActiveSubtree->vertex.edges[sourceActiveSubtree->edgeIndex];
+
+                ActiveSubtree& destinationActiveSubtree = stack[stackIndex];
+                destinationActiveSubtree.nfaNode = edge.term->generateGraph(nfa, sourceNode, actions.get(&destinationActiveSubtree.vertex));
+
+                sourceActiveSubtree = &destinationActiveSubtree;
+            }
+
+            return;
+        }
+    }
+}
+
+static void generateSuffixWithReverseSuffixTree(NFA& nfa, Vector<ActiveSubtree>& stack, const HashMap<const PrefixTreeVertex*, ActionList>& actions, ReverseSuffixTreeRoots& reverseSuffixTreeRoots)
+{
+    ActiveSubtree& leafSubtree = stack.last();
+    ASSERT_WITH_MESSAGE(!leafSubtree.nfaNode.isValid(), "The leaf should never be generated by the code above, it should always be inserted into the prefix tree.");
+
+    ActionList actionList = actions.get(&leafSubtree.vertex);
+    ASSERT_WITH_MESSAGE(!actionList.isEmpty(), "Our prefix tree should always have actions on the leaves by construction.");
+
+    HashableActionList hashableActionList(actionList);
+    auto rootAddResult = reverseSuffixTreeRoots.add(hashableActionList, ReverseSuffixTreeVertex());
+    if (rootAddResult.isNewEntry) {
+        ImmutableCharNFANodeBuilder newNode(nfa);
+        newNode.setActions(actionList.begin(), actionList.end());
+        rootAddResult.iterator->value.nodeId = newNode.nodeId();
+    }
+
+    ReverseSuffixTreeVertex* activeReverseSuffixTreeVertex = &rootAddResult.iterator->value;
+    uint32_t destinationNodeId = rootAddResult.iterator->value.nodeId;
+
+    unsigned stackPosition = stack.size() - 2;
+    while (true) {
+        ActiveSubtree& source = stack[stackPosition];
+        auto& edge = source.vertex.edges[source.edgeIndex];
+
+        // This is the end condition: when we meet a node that has already been generated,
+        // we just need to connect our backward tree to the forward tree.
+        //
+        // We *must not* add this last node to the reverse-suffix tree. That node can have
+        // transitions back to earlier part of the prefix tree. If the prefix tree "caches"
+        // such node, it would create new transitions that did not exist in the source language.
+        if (source.nfaNode.isValid()) {
+            stack.shrink(stackPosition + 1);
+            edge.term->generateGraph(nfa, source.nfaNode, destinationNodeId);
+            return;
+        }
+        --stackPosition;
+
+        ASSERT_WITH_MESSAGE(!actions.contains(&source.vertex), "Any node with final actions should have been created before hitting the reverse suffix-tree.");
+
+        ReverseSuffixTreeEdge* existingEdge = nullptr;
+        for (ReverseSuffixTreeEdge& potentialExistingEdge : activeReverseSuffixTreeVertex->edges) {
+            if (edge.term == potentialExistingEdge.term) {
+                existingEdge = &potentialExistingEdge;
+                break;
+            }
+        }
+
+        if (existingEdge)
+            activeReverseSuffixTreeVertex = existingEdge->child.get();
+        else {
+            ImmutableCharNFANodeBuilder newNode(nfa);
+            edge.term->generateGraph(nfa, newNode, destinationNodeId);
+            std::unique_ptr<ReverseSuffixTreeVertex> newVertex(new ReverseSuffixTreeVertex());
+            newVertex->nodeId = newNode.nodeId();
+
+            ReverseSuffixTreeVertex* newVertexAddress = newVertex.get();
+            activeReverseSuffixTreeVertex->edges.append(ReverseSuffixTreeEdge({ edge.term, WTF::move(newVertex) }));
+            activeReverseSuffixTreeVertex = newVertexAddress;
+        }
+        destinationNodeId = activeReverseSuffixTreeVertex->nodeId;
+
+        ASSERT(source.vertex.edges.size() == 1);
+        source.vertex.edges.clear();
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+static void clearReverseSuffixTree(ReverseSuffixTreeRoots& reverseSuffixTreeRoots)
+{
+    // We cannot rely on the destructor being called in order from top to bottom as we may overflow
+    // the stack. Instead, we go depth first in the reverse-suffix-tree.
+
+    for (auto& slot : reverseSuffixTreeRoots) {
+        Vector<ReverseSuffixTreeVertex*, 128> stack;
+        stack.append(&slot.value);
+
+        while (true) {
+            ReverseSuffixTreeVertex* top = stack.last();
+            if (top->edges.isEmpty()) {
+                stack.removeLast();
+                if (stack.isEmpty())
+                    break;
+                stack.last()->edges.removeLast();
+            } else
+                stack.append(top->edges.last().child.get());
+        }
+    }
+    reverseSuffixTreeRoots.clear();
+}
+
+static void generateNFAForSubtree(NFA& nfa, ImmutableCharNFANodeBuilder&& subtreeRoot, PrefixTreeVertex& root, const HashMap<const PrefixTreeVertex*, ActionList>& actions, size_t maxNFASize)
+{
     // This recurses the subtree of the prefix tree.
     // For each edge that has fixed length (no quantifiers like ?, *, or +) it generates the nfa graph,
     // recurses into children, and deletes any processed leaf nodes.
-    struct ActiveSubtree {
-        ActiveSubtree(PrefixTreeVertex& vertex, ImmutableCharNFANodeBuilder&& nfaNode, unsigned edgeIndex)
-            : vertex(vertex)
-            , nfaNode(WTF::move(nfaNode))
-            , edgeIndex(edgeIndex)
-        {
-        }
-        PrefixTreeVertex& vertex;
-        ImmutableCharNFANodeBuilder nfaNode;
-        unsigned edgeIndex;
-    };
+
+    ReverseSuffixTreeRoots reverseSuffixTreeRoots;
     Vector<ActiveSubtree> stack;
     if (!root.edges.isEmpty())
         stack.append(ActiveSubtree(root, WTF::move(subtreeRoot), 0));
+
     bool nfaTooBig = false;
     
     // Generate graphs for each subtree that does not contain any quantifiers.
     while (!stack.isEmpty()) {
         PrefixTreeVertex& vertex = stack.last().vertex;
         const unsigned edgeIndex = stack.last().edgeIndex;
-        
-        // Only stop generating an NFA at a leaf to ensure we have a correct NFA. We could go slightly over the maxNFASize.
-        if (vertex.edges.isEmpty() && nfa.nodes.size() > maxNFASize)
-            nfaTooBig = true;
-        
+
         if (edgeIndex < vertex.edges.size()) {
             auto& edge = vertex.edges[edgeIndex];
-            
+
             // Clean up any processed leaves and return early if we are past the maxNFASize.
             if (nfaTooBig) {
                 stack.last().edgeIndex = stack.last().vertex.edges.size();
@@ -251,17 +412,28 @@
                 continue;
             }
 
-
-            ImmutableCharNFANodeBuilder newNode = edge.term->generateGraph(nfa, stack.last().nfaNode, actions.get(edge.child.get()));
             ASSERT(edge.child.get());
-            stack.append(ActiveSubtree(*edge.child.get(), WTF::move(newNode), 0));
+            ImmutableCharNFANodeBuilder emptyBuilder;
+            stack.append(ActiveSubtree(*edge.child.get(), WTF::move(emptyBuilder), 0));
         } else {
+            bool isLeaf = vertex.edges.isEmpty();
+
             ASSERT(edgeIndex == vertex.edges.size());
             vertex.edges.removeAllMatching([](PrefixTreeEdge& edge)
             {
                 return !edge.term;
             });
-            stack.removeLast();
+
+            if (isLeaf) {
+                generateInfixUnsuitableForReverseSuffixTree(nfa, stack, actions);
+                generateSuffixWithReverseSuffixTree(nfa, stack, actions, reverseSuffixTreeRoots);
+
+                // Only stop generating an NFA at a leaf to ensure we have a correct NFA. We could go slightly over the maxNFASize.
+                if (nfa.nodes.size() > maxNFASize)
+                    nfaTooBig = true;
+            } else
+                stack.removeLast();
+
             if (!stack.isEmpty()) {
                 auto& activeSubtree = stack.last();
                 auto& edge = activeSubtree.vertex.edges[stack.last().edgeIndex];
@@ -271,6 +443,7 @@
             }
         }
     }
+    clearReverseSuffixTree(reverseSuffixTreeRoots);
 }
 
 void CombinedURLFilters::processNFAs(size_t maxNFASize, std::function<void(NFA&&)> handler)

Modified: branches/safari-601.1-branch/Source/WebCore/contentextensions/DFA.cpp (187086 => 187087)


--- branches/safari-601.1-branch/Source/WebCore/contentextensions/DFA.cpp	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Source/WebCore/contentextensions/DFA.cpp	2015-07-21 05:08:21 UTC (rev 187087)
@@ -147,7 +147,7 @@
                 dataLogF("%llu", actions[actionIndex]);
             }
         }
-        dataLogF("]");
+        dataLogF(">]");
 
         if (!actions.isEmpty())
             dataLogF(" [shape=doublecircle]");

Added: branches/safari-601.1-branch/Source/WebCore/contentextensions/HashableActionList.h (0 => 187087)


--- branches/safari-601.1-branch/Source/WebCore/contentextensions/HashableActionList.h	                        (rev 0)
+++ branches/safari-601.1-branch/Source/WebCore/contentextensions/HashableActionList.h	2015-07-21 05:08:21 UTC (rev 187087)
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef HashableActionList_h
+#define HashableActionList_h
+
+#include <wtf/StringHasher.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+namespace ContentExtensions {
+
+struct HashableActionList {
+    enum DeletedValueTag { DeletedValue };
+    explicit HashableActionList(DeletedValueTag) { state = Deleted; }
+
+    enum EmptyValueTag { EmptyValue };
+    explicit HashableActionList(EmptyValueTag) { state = Empty; }
+
+    template<typename AnyVectorType>
+    explicit HashableActionList(const AnyVectorType& otherActions)
+        : actions(otherActions)
+        , state(Valid)
+    {
+        std::sort(actions.begin(), actions.end());
+        StringHasher hasher;
+        hasher.addCharactersAssumingAligned(reinterpret_cast<const UChar*>(actions.data()), actions.size() * sizeof(uint64_t) / sizeof(UChar));
+        hash = hasher.hash();
+    }
+
+    bool isEmptyValue() const { return state == Empty; }
+    bool isDeletedValue() const { return state == Deleted; }
+
+    bool operator==(const HashableActionList other) const
+    {
+        return state == other.state && actions == other.actions;
+    }
+
+    bool operator!=(const HashableActionList other) const
+    {
+        return !(*this == other);
+    }
+
+    Vector<uint64_t> actions;
+    unsigned hash;
+    enum {
+        Valid,
+        Empty,
+        Deleted
+    } state;
+};
+
+struct HashableActionListHash {
+    static unsigned hash(const HashableActionList& actionKey)
+    {
+        return actionKey.hash;
+    }
+
+    static bool equal(const HashableActionList& a, const HashableActionList& b)
+    {
+        return a == b;
+    }
+    static const bool safeToCompareToEmptyOrDeleted = false;
+};
+
+struct HashableActionListHashTraits : public WTF::CustomHashTraits<HashableActionList> {
+    static const bool emptyValueIsZero = false;
+};
+
+} // namespace ContentExtensions
+
+} // namespace WebCore
+
+#endif

Modified: branches/safari-601.1-branch/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h (187086 => 187087)


--- branches/safari-601.1-branch/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h	2015-07-21 05:08:21 UTC (rev 187087)
@@ -52,9 +52,6 @@
     {
         m_nodeId = m_immutableNFA->nodes.size();
         m_immutableNFA->nodes.append(ImmutableNFANode());
-#if !ASSERT_DISABLED
-        m_isDisconnected = true;
-#endif
     }
 
     ImmutableNFANodeBuilder(ImmutableNFANodeBuilder&& other)
@@ -64,25 +61,28 @@
         , m_actions(WTF::move(other.m_actions))
         , m_nodeId(other.m_nodeId)
         , m_finalized(other.m_finalized)
-#if !ASSERT_DISABLED
-        , m_isDisconnected(other.m_isDisconnected)
-#endif
     {
         other.m_immutableNFA = nullptr;
         other.m_finalized = true;
-#if !ASSERT_DISABLED
-        other.m_isDisconnected = false;
-#endif
     }
 
     ~ImmutableNFANodeBuilder()
     {
-        ASSERT_WITH_MESSAGE(!m_isDisconnected, "This nodes is not connected to anything and is not reached by any other node.");
-
         if (!m_finalized)
             finalize();
     }
 
+    bool isValid() const
+    {
+        return !!m_immutableNFA;
+    }
+
+    uint32_t nodeId() const
+    {
+        ASSERT(isValid());
+        return m_nodeId;
+    }
+
     struct TrivialRange {
         CharacterType first;
         CharacterType last;
@@ -108,15 +108,10 @@
         bool isEnd;
     };
 
-    void addTransition(CharacterType first, CharacterType last, const ImmutableNFANodeBuilder& target)
+    void addTransition(CharacterType first, CharacterType last, uint32_t targetNodeId)
     {
         ASSERT(!m_finalized);
         ASSERT(m_immutableNFA);
-        ASSERT(m_immutableNFA == target.m_immutableNFA);
-#if !ASSERT_DISABLED
-        m_isDisconnected = false;
-        target.m_isDisconnected = false;
-#endif
 
         struct Converter {
             TargetSet convert(uint32_t target)
@@ -130,19 +125,20 @@
         };
         
         Converter converter;
-        m_ranges.extend(FakeRangeIterator { { first, last }, target.m_nodeId, false }, FakeRangeIterator { { 0, 0 }, target.m_nodeId, true }, converter);
+        m_ranges.extend(FakeRangeIterator { { first, last }, targetNodeId, false }, FakeRangeIterator { { 0, 0 }, targetNodeId, true }, converter);
     }
 
     void addEpsilonTransition(const ImmutableNFANodeBuilder& target)
     {
+        ASSERT(m_immutableNFA == target.m_immutableNFA);
+        addEpsilonTransition(target.m_nodeId);
+    }
+
+    void addEpsilonTransition(uint32_t targetNodeId)
+    {
         ASSERT(!m_finalized);
         ASSERT(m_immutableNFA);
-        ASSERT(m_immutableNFA == target.m_immutableNFA);
-#if !ASSERT_DISABLED
-        m_isDisconnected = false;
-        target.m_isDisconnected = false;
-#endif
-        m_epsilonTransitionTargets.add(target.m_nodeId);
+        m_epsilonTransitionTargets.add(targetNodeId);
     }
 
     template<typename ActionIterator>
@@ -168,10 +164,6 @@
 
         other.m_immutableNFA = nullptr;
         other.m_finalized = true;
-#if !ASSERT_DISABLED
-        m_isDisconnected = other.m_isDisconnected;
-        other.m_isDisconnected = false;
-#endif
         return *this;
     }
 
@@ -230,9 +222,6 @@
     HashSet<ActionType, WTF::IntHash<ActionType>, WTF::UnsignedWithZeroKeyHashTraits<ActionType>> m_actions;
     uint32_t m_nodeId;
     bool m_finalized { true };
-#if !ASSERT_DISABLED
-    mutable bool m_isDisconnected { false };
-#endif
 };
 
 }

Modified: branches/safari-601.1-branch/Source/WebCore/contentextensions/Term.h (187086 => 187087)


--- branches/safari-601.1-branch/Source/WebCore/contentextensions/Term.h	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Source/WebCore/contentextensions/Term.h	2015-07-21 05:08:21 UTC (rev 187087)
@@ -83,7 +83,8 @@
 
     void quantify(const AtomQuantifier&);
 
-    ImmutableCharNFANodeBuilder generateGraph(NFA&, ImmutableCharNFANodeBuilder& start, const ActionList& finalActions) const;
+    ImmutableCharNFANodeBuilder generateGraph(NFA&, ImmutableCharNFANodeBuilder& source, const ActionList& finalActions) const;
+    void generateGraph(NFA&, ImmutableCharNFANodeBuilder& source, uint32_t destination) const;
 
     bool isEndOfLineAssertion() const;
 
@@ -118,7 +119,8 @@
     // The return value can be false for a group equivalent to a universal transition.
     bool isUniversalTransition() const;
 
-    ImmutableCharNFANodeBuilder generateSubgraphForAtom(NFA&, ImmutableCharNFANodeBuilder& source) const;
+    void generateSubgraphForAtom(NFA&, ImmutableCharNFANodeBuilder& source, const ImmutableCharNFANodeBuilder& destination) const;
+    void generateSubgraphForAtom(NFA&, ImmutableCharNFANodeBuilder& source, uint32_t destination) const;
 
     void destroy();
 
@@ -377,50 +379,52 @@
     m_quantifier = quantifier;
 }
 
-inline ImmutableCharNFANodeBuilder Term::generateGraph(NFA& nfa, ImmutableCharNFANodeBuilder& start, const ActionList& finalActions) const
+inline ImmutableCharNFANodeBuilder Term::generateGraph(NFA& nfa, ImmutableCharNFANodeBuilder& source, const ActionList& finalActions) const
 {
+    ImmutableCharNFANodeBuilder newEnd(nfa);
+    generateGraph(nfa, source, newEnd.nodeId());
+    newEnd.setActions(finalActions.begin(), finalActions.end());
+    return newEnd;
+}
+
+inline void Term::generateGraph(NFA& nfa, ImmutableCharNFANodeBuilder& source, uint32_t destination) const
+{
     ASSERT(isValid());
 
-    ImmutableCharNFANodeBuilder newEnd;
-
     switch (m_quantifier) {
     case AtomQuantifier::One: {
-        newEnd = generateSubgraphForAtom(nfa, start);
+        generateSubgraphForAtom(nfa, source, destination);
         break;
     }
     case AtomQuantifier::ZeroOrOne: {
-        newEnd = generateSubgraphForAtom(nfa, start);
-        start.addEpsilonTransition(newEnd);
+        generateSubgraphForAtom(nfa, source, destination);
+        source.addEpsilonTransition(destination);
         break;
     }
     case AtomQuantifier::ZeroOrMore: {
         ImmutableCharNFANodeBuilder repeatStart(nfa);
-        start.addEpsilonTransition(repeatStart);
+        source.addEpsilonTransition(repeatStart);
 
-        ImmutableCharNFANodeBuilder repeatEnd = generateSubgraphForAtom(nfa, repeatStart);
+        ImmutableCharNFANodeBuilder repeatEnd(nfa);
+        generateSubgraphForAtom(nfa, repeatStart, repeatEnd);
         repeatEnd.addEpsilonTransition(repeatStart);
 
-        ImmutableCharNFANodeBuilder kleenEnd(nfa);
-        repeatEnd.addEpsilonTransition(kleenEnd);
-        start.addEpsilonTransition(kleenEnd);
-        newEnd = WTF::move(kleenEnd);
+        repeatEnd.addEpsilonTransition(destination);
+        source.addEpsilonTransition(destination);
         break;
     }
     case AtomQuantifier::OneOrMore: {
         ImmutableCharNFANodeBuilder repeatStart(nfa);
-        start.addEpsilonTransition(repeatStart);
+        source.addEpsilonTransition(repeatStart);
 
-        ImmutableCharNFANodeBuilder repeatEnd = generateSubgraphForAtom(nfa, repeatStart);
+        ImmutableCharNFANodeBuilder repeatEnd(nfa);
+        generateSubgraphForAtom(nfa, repeatStart, repeatEnd);
         repeatEnd.addEpsilonTransition(repeatStart);
 
-        ImmutableCharNFANodeBuilder afterRepeat(nfa);
-        repeatEnd.addEpsilonTransition(afterRepeat);
-        newEnd = WTF::move(afterRepeat);
+        repeatEnd.addEpsilonTransition(destination);
         break;
     }
     }
-    newEnd.setActions(finalActions.begin(), finalActions.end());
-    return newEnd;
 }
 
 inline bool Term::isEndOfLineAssertion() const
@@ -582,14 +586,19 @@
     return false;
 }
 
-inline ImmutableCharNFANodeBuilder Term::generateSubgraphForAtom(NFA& nfa, ImmutableCharNFANodeBuilder& source) const
+inline void Term::generateSubgraphForAtom(NFA& nfa, ImmutableCharNFANodeBuilder& source, const ImmutableCharNFANodeBuilder& destination) const
 {
+    generateSubgraphForAtom(nfa, source, destination.nodeId());
+}
+
+inline void Term::generateSubgraphForAtom(NFA& nfa, ImmutableCharNFANodeBuilder& source, uint32_t destination) const
+{
     switch (m_termType) {
     case TermType::Empty:
         ASSERT_NOT_REACHED();
-        return ImmutableCharNFANodeBuilder();
+        source.addEpsilonTransition(destination);
+        break;
     case TermType::CharacterSet: {
-        ImmutableCharNFANodeBuilder newNode(nfa);
         if (!m_atomData.characterSet.inverted()) {
             UChar i = 0;
             while (true) {
@@ -602,7 +611,7 @@
                 ++i;
                 while (i < 128 && m_atomData.characterSet.get(i))
                     ++i;
-                source.addTransition(start, i - 1, newNode);
+                source.addTransition(start, i - 1, destination);
             }
         } else {
             UChar i = 1;
@@ -616,27 +625,32 @@
                 ++i;
                 while (i < 128 && !m_atomData.characterSet.get(i))
                     ++i;
-                source.addTransition(start, i - 1, newNode);
+                source.addTransition(start, i - 1, destination);
             }
         }
-        return newNode;
+        break;
     }
     case TermType::Group: {
         if (m_atomData.group.terms.isEmpty()) {
             // FIXME: any kind of empty term could be avoided in the parser. This case should turned into an assertion.
-            ImmutableCharNFANodeBuilder newNode(nfa);
-            source.addEpsilonTransition(newNode);
-            return newNode;
+            source.addEpsilonTransition(destination);
+            return;
         }
 
+        if (m_atomData.group.terms.size() == 1) {
+            m_atomData.group.terms.first().generateGraph(nfa, source, destination);
+            return;
+        }
+
         ImmutableCharNFANodeBuilder lastTarget = m_atomData.group.terms.first().generateGraph(nfa, source, ActionList());
-        for (unsigned i = 1; i < m_atomData.group.terms.size(); ++i) {
+        for (unsigned i = 1; i < m_atomData.group.terms.size() - 1; ++i) {
             const Term& currentTerm = m_atomData.group.terms[i];
             ImmutableCharNFANodeBuilder newNode = currentTerm.generateGraph(nfa, lastTarget, ActionList());
             lastTarget = WTF::move(newNode);
         }
-
-        return lastTarget;
+        const Term& lastTerm = m_atomData.group.terms.last();
+        lastTerm.generateGraph(nfa, lastTarget, destination);
+        break;
     }
     }
 }

Modified: branches/safari-601.1-branch/Tools/ChangeLog (187086 => 187087)


--- branches/safari-601.1-branch/Tools/ChangeLog	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Tools/ChangeLog	2015-07-21 05:08:21 UTC (rev 187087)
@@ -1,5 +1,20 @@
 2015-07-20  Matthew Hanson  <matthew_han...@apple.com>
 
+        Merge r186910. rdar://problem/21863296
+
+    2015-07-16  Benjamin Poulain  <bpoul...@apple.com>
+
+            [Content extensions] Combine suffixes when generating NFAs
+            https://bugs.webkit.org/show_bug.cgi?id=146961
+
+            Reviewed by Alex Christensen.
+
+            * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+            (TestWebKitAPI::compareContents):
+            * TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp:
+
+2015-07-20  Matthew Hanson  <matthew_han...@apple.com>
+
         Merge r186982. rdar://problem/21567820
 
     2015-07-17  Andy Estes  <aes...@apple.com>

Modified: branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp (187086 => 187087)


--- branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp	2015-07-21 05:08:21 UTC (rev 187087)
@@ -95,6 +95,103 @@
     EXPECT_EQ(4, vector[3]);
 }
 
+TEST(WTF_Vector, InitializeFromOtherInitialCapacity)
+{
+    Vector<int, 3> vector = { 1, 3, 2, 4 };
+    Vector<int, 5> vectorCopy(vector);
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, vectorCopy.size());
+    EXPECT_EQ(5U, vectorCopy.capacity());
+
+    EXPECT_EQ(1, vectorCopy[0]);
+    EXPECT_EQ(3, vectorCopy[1]);
+    EXPECT_EQ(2, vectorCopy[2]);
+    EXPECT_EQ(4, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, CopyFromOtherInitialCapacity)
+{
+    Vector<int, 3> vector = { 1, 3, 2, 4 };
+    Vector<int, 5> vectorCopy { 0 };
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(1U, vectorCopy.size());
+
+    vectorCopy = vector;
+
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, vectorCopy.size());
+    EXPECT_EQ(5U, vectorCopy.capacity());
+
+    EXPECT_EQ(1, vectorCopy[0]);
+    EXPECT_EQ(3, vectorCopy[1]);
+    EXPECT_EQ(2, vectorCopy[2]);
+    EXPECT_EQ(4, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, InitializeFromOtherOverflowBehavior)
+{
+    Vector<int, 7, WTF::CrashOnOverflow> vector = { 4, 3, 2, 1 };
+    Vector<int, 7, UnsafeVectorOverflow> vectorCopy(vector);
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, vectorCopy.size());
+
+    EXPECT_EQ(4, vectorCopy[0]);
+    EXPECT_EQ(3, vectorCopy[1]);
+    EXPECT_EQ(2, vectorCopy[2]);
+    EXPECT_EQ(1, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, CopyFromOtherOverflowBehavior)
+{
+    Vector<int, 7, WTF::CrashOnOverflow> vector = { 4, 3, 2, 1 };
+    Vector<int, 7, UnsafeVectorOverflow> vectorCopy = { 0, 0, 0 };
+
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(3U, vectorCopy.size());
+
+    vectorCopy = vector;
+
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, vectorCopy.size());
+
+    EXPECT_EQ(4, vectorCopy[0]);
+    EXPECT_EQ(3, vectorCopy[1]);
+    EXPECT_EQ(2, vectorCopy[2]);
+    EXPECT_EQ(1, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, InitializeFromOtherMinCapacity)
+{
+    Vector<int, 7, WTF::CrashOnOverflow, 1> vector = { 3, 4, 2, 1 };
+    Vector<int, 7, WTF::CrashOnOverflow, 50> vectorCopy(vector);
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, vectorCopy.size());
+
+    EXPECT_EQ(3, vectorCopy[0]);
+    EXPECT_EQ(4, vectorCopy[1]);
+    EXPECT_EQ(2, vectorCopy[2]);
+    EXPECT_EQ(1, vectorCopy[3]);
+}
+
+TEST(WTF_Vector, CopyFromOtherMinCapacity)
+{
+    Vector<int, 7, WTF::CrashOnOverflow, 1> vector = { 3, 4, 2, 1 };
+    Vector<int, 7, WTF::CrashOnOverflow, 50> vectorCopy;
+
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(0U, vectorCopy.size());
+
+    vectorCopy = vector;
+
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, vectorCopy.size());
+
+    EXPECT_EQ(3, vectorCopy[0]);
+    EXPECT_EQ(4, vectorCopy[1]);
+    EXPECT_EQ(2, vectorCopy[2]);
+    EXPECT_EQ(1, vectorCopy[3]);
+}
+
 TEST(WTF_Vector, Reverse)
 {
     Vector<int> intVector;

Modified: branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (187086 => 187087)


--- branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp	2015-07-21 05:08:21 UTC (rev 187087)
@@ -235,6 +235,36 @@
     testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
 }
 
+TEST_F(ContentExtensionTest, SingleCharacter)
+{
+    auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^z\"}}]");
+    testRequest(matchBackend, mainDocumentRequest("http://webkit.org/"), { });
+    testRequest(matchBackend, mainDocumentRequest("zttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+    auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"y\"}}]");
+    testRequest(searchBackend, mainDocumentRequest("http://webkit.org/"), { });
+    testRequest(searchBackend, mainDocumentRequest("http://webkit.org/ywebkit"), { ContentExtensions::ActionType::BlockLoad });
+}
+
+TEST_F(ContentExtensionTest, SingleCharacterDisjunction)
+{
+    auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^z\"}},"
+        "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^c\"}}]");
+    testRequest(matchBackend, mainDocumentRequest("http://webkit.org/"), { });
+    testRequest(matchBackend, mainDocumentRequest("bttp://webkit.org/"), { });
+    testRequest(matchBackend, mainDocumentRequest("cttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest("dttp://webkit.org/"), { });
+    testRequest(matchBackend, mainDocumentRequest("zttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+    auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"x\"}},"
+        "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"y\"}}]");
+    testRequest(searchBackend, mainDocumentRequest("http://webkit.org/"), { });
+    testRequest(searchBackend, mainDocumentRequest("http://webkit.org/dwebkit"), { });
+    testRequest(searchBackend, mainDocumentRequest("http://webkit.org/xwebkit"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest("http://webkit.org/ywebkit"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest("http://webkit.org/zwebkit"), { });
+}
+
 TEST_F(ContentExtensionTest, RangeBasic)
 {
     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"w[0-9]c\", \"url-filter-is-case-sensitive\":true}},"
@@ -444,6 +474,142 @@
     testRequest(backend, mainDocumentRequest("https://post.org/post"), { });
 }
 
+TEST_F(ContentExtensionTest, UndistinguishableActionInsidePrefixTree)
+{
+    // In this case, the two actions are undistinguishable. The actions of "prefix" appear inside the prefixtree
+    // ending at "suffix".
+    auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefix\"}},"
+        "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefixsuffix\"}}]");
+
+    testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://prefix.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/prefix"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/aaaprefixaaa"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://prefixsuffix.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/prefixsuffix"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/bbbprefixsuffixbbb"), { ContentExtensions::ActionType::BlockLoad });
+
+    testRequest(backend, mainDocumentRequest("http://suffix.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/suffix"), { });
+}
+
+TEST_F(ContentExtensionTest, DistinguishableActionInsidePrefixTree)
+{
+    // In this case, the two actions are distinguishable.
+    auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefix\"}},"
+        "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"prefixsuffix\"}}]");
+
+    testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://prefix.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/prefix"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/aaaprefixaaa"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://prefixsuffix.org/"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/prefixsuffix"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/bbbprefixsuffixbbb"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
+
+    testRequest(backend, mainDocumentRequest("http://suffix.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://webkit.org/suffix"), { });
+}
+
+TEST_F(ContentExtensionTest, DistinguishablePrefixAreNotMerged)
+{
+    auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foo\\\\.org\"}},"
+        "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar\\\\.org\"}}]");
+
+    testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://foo.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://bar.org/"), { ContentExtensions::ActionType::BlockLoad });
+
+    testRequest(backend, mainDocumentRequest("http://foor.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://fooar.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://fooba.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://foob.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://foor.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://foar.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://foba.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://fob.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://barf.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://barfo.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://baroo.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://baro.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://baf.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://bafo.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://baoo.org/"), { });
+    testRequest(backend, mainDocumentRequest("http://bao.org/"), { });
+
+    testRequest(backend, mainDocumentRequest("http://foo.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://oo.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://o.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://rgbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://gbar.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://foo.orgar.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://foo.orgr.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://foo.org.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://foo.orgorg/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://foo.orgrg/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(backend, mainDocumentRequest("http://foo.orgg/"), { ContentExtensions::ActionType::BlockLoad });
+}
+
+static void compareContents(const ContentExtensions::DFABytecodeInterpreter::Actions& a, const Vector<uint64_t>& b)
+{
+    EXPECT_EQ(a.size(), b.size());
+    for (unsigned i = 0; i < b.size(); ++i)
+        EXPECT_TRUE(a.contains(b[i]));
+}
+
+TEST_F(ContentExtensionTest, SearchSuffixesWithIdenticalActionAreMerged)
+{
+    ContentExtensions::CombinedURLFilters combinedURLFilters;
+    ContentExtensions::URLFilterParser parser(combinedURLFilters);
+    EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("foo\\.org", false, 0));
+    EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("ba\\.org", false, 0));
+
+    Vector<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters);
+    EXPECT_EQ(1ul, nfas.size());
+    EXPECT_EQ(12ul, nfas.first().nodes.size());
+
+    ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first());
+    Vector<ContentExtensions::DFABytecode> bytecode;
+    ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
+    compiler.compile();
+    ContentExtensions::DFABytecodeInterpreter interpreter(bytecode.data(), bytecode.size());
+    compareContents(interpreter.interpret("foo.org", 0), { 0 });
+    compareContents(interpreter.interpret("ba.org", 0), { 0 });
+    compareContents(interpreter.interpret("bar.org", 0), { });
+
+    compareContents(interpreter.interpret("paddingfoo.org", 0), { 0 });
+    compareContents(interpreter.interpret("paddingba.org", 0), { 0 });
+    compareContents(interpreter.interpret("paddingbar.org", 0), { });
+}
+
+TEST_F(ContentExtensionTest, SearchSuffixesWithDistinguishableActionAreNotMerged)
+{
+    ContentExtensions::CombinedURLFilters combinedURLFilters;
+    ContentExtensions::URLFilterParser parser(combinedURLFilters);
+    EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("foo\\.org", false, 0));
+    EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("ba\\.org", false, 1));
+
+    Vector<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters);
+
+    EXPECT_EQ(1ul, nfas.size());
+    EXPECT_EQ(17ul, nfas.first().nodes.size());
+
+    ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first());
+    Vector<ContentExtensions::DFABytecode> bytecode;
+    ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode);
+    compiler.compile();
+    ContentExtensions::DFABytecodeInterpreter interpreter(bytecode.data(), bytecode.size());
+    compareContents(interpreter.interpret("foo.org", 0), { 0 });
+    compareContents(interpreter.interpret("ba.org", 0), { 1 });
+    compareContents(interpreter.interpret("bar.org", 0), { });
+
+    compareContents(interpreter.interpret("paddingfoo.org", 0), { 0 });
+    compareContents(interpreter.interpret("paddingba.org", 0), { 1 });
+    compareContents(interpreter.interpret("paddingba.orgfoo.org", 0), { 1, 0 });
+    compareContents(interpreter.interpret("paddingbar.org", 0), { });
+}
+
 TEST_F(ContentExtensionTest, DomainTriggers)
 {
     auto ifDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\"]}}]");

Modified: branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp (187086 => 187087)


--- branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp	2015-07-21 05:08:13 UTC (rev 187086)
+++ branches/safari-601.1-branch/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp	2015-07-21 05:08:21 UTC (rev 187087)
@@ -41,15 +41,31 @@
 TEST_F(DFAMinimizerTest, BasicSearch)
 {
     ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*foo", ".*bar", ".*bang"});
-    EXPECT_EQ(static_cast<size_t>(10), countLiveNodes(dfa));
+    EXPECT_EQ(static_cast<size_t>(8), countLiveNodes(dfa));
     dfa.minimize();
     EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa));
 }
 
+TEST_F(DFAMinimizerTest, MergeSuffixes)
+{
+    ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*aaa", ".*aab", ".*aba", ".*abb", ".*baa", ".*bab", ".*bba", ".*bbb"});
+    EXPECT_EQ(static_cast<size_t>(12), countLiveNodes(dfa));
+    dfa.minimize();
+    EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(dfa));
+}
+
+TEST_F(DFAMinimizerTest, MergeInfixes)
+{
+    ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*aaakit", ".*aabkit", ".*abakit", ".*abbkit", ".*baakit", ".*babkit", ".*bbakit", ".*bbbkit"});
+    EXPECT_EQ(static_cast<size_t>(15), countLiveNodes(dfa));
+    dfa.minimize();
+    EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa));
+}
+
 TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge1)
 {
     ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.a", "^b.a", "^bac", "^bbc", "^BCC"});
-    EXPECT_EQ(static_cast<size_t>(13), countLiveNodes(dfa));
+    EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
     dfa.minimize();
     EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
 }
@@ -57,7 +73,7 @@
 TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge2)
 {
     ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^bbc", "^BCC", "^a.a", "^b.a"});
-    EXPECT_EQ(static_cast<size_t>(11), countLiveNodes(dfa));
+    EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
     dfa.minimize();
     EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
 }
@@ -65,7 +81,7 @@
 TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge3)
 {
     ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.c", "^b.c", "^baa", "^bba", "^BCA"});
-    EXPECT_EQ(static_cast<size_t>(13), countLiveNodes(dfa));
+    EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
     dfa.minimize();
     EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
 }
@@ -73,7 +89,7 @@
 TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge4)
 {
     ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^baa", "^bba", "^BCA", "^a.c", "^b.c"});
-    EXPECT_EQ(static_cast<size_t>(13), countLiveNodes(dfa));
+    EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
     dfa.minimize();
     EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa));
 }
@@ -81,7 +97,7 @@
 TEST_F(DFAMinimizerTest, FallbackTransitionsToOtherNodeInSameGroupDoesNotDifferentiateGroup)
 {
     ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^aac", "^a.c", "^b.c"});
-    EXPECT_EQ(static_cast<size_t>(9), countLiveNodes(dfa));
+    EXPECT_EQ(static_cast<size_t>(5), countLiveNodes(dfa));
     dfa.minimize();
     EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(dfa));
 }
@@ -89,7 +105,7 @@
 TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator1)
 {
     ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.bc.de", "^a.bd.ef"});
-    EXPECT_EQ(static_cast<size_t>(12), countLiveNodes(dfa));
+    EXPECT_EQ(static_cast<size_t>(11), countLiveNodes(dfa));
     dfa.minimize();
     EXPECT_EQ(static_cast<size_t>(11), countLiveNodes(dfa));
 }
@@ -97,7 +113,7 @@
 TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator2)
 {
     ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^cb.", "^db.b"});
-    EXPECT_EQ(static_cast<size_t>(8), countLiveNodes(dfa));
+    EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa));
     dfa.minimize();
     EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa));
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to