From cff63ad5d5490d3666bde49d0a7992ea44ffc2c7 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Thu, 21 Mar 2024 12:31:58 +0100 Subject: [PATCH] Python: Fix small join-order problem for call-graph problem is: ``` 14294 ~33% {1} r23 = r21 UNION r22 13626 ~0% {2} | JOIN WITH `DataFlowPublic::Node.getEnclosingCallable/0#dispred#be95825a` ON FIRST 1 OUTPUT Rhs.1, Lhs.0 11871493 ~2% {2} | JOIN WITH `DataFlowPublic::Node.getEnclosingCallable/0#dispred#be95825a_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 6810938 ~3% {2} | JOIN WITH num#DataFlowPublic::TCfgNode#2cd2fb22_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.1 0 ~0% {4} | JOIN WITH `DataFlowDispatch::resolveMethodCall/4#3067f1f1#reorder_0_3_1_2#prev` ON FIRST 2 OUTPUT Rhs.3, Lhs.1, Lhs.0, Rhs.2 0 ~0% {4} | JOIN WITH num#DataFlowDispatch::CallTypeClassMethod#3508c3e5 ON FIRST 1 OUTPUT Lhs.3, Lhs.2, Lhs.0, Lhs.1 0 ~0% {4} | JOIN WITH `DataFlowDispatch::resolveCall/3#454c02d8#reorder_1_0_2#prev` ON FIRST 3 OUTPUT Lhs.3, Lhs.1, Lhs.0, Lhs.2 0 ~0% {5} | JOIN WITH num#DataFlowDispatch::TSelfArgumentPosition#de6d64b8 CARTESIAN PRODUCT OUTPUT Lhs.1, Lhs.2, Lhs.3, Lhs.0, Rhs.0 ``` that is, it does cartesian product of DataFlowPublic::Node.getEnclosingCallable After fix ``` 14294 ~33% {1} r23 = r21 UNION r22 0 ~0% {4} | JOIN WITH `DataFlowDispatch::resolveMethodCall/4#3067f1f1#reorder_3_0_1_2#prev` ON FIRST 1 OUTPUT Rhs.3, Lhs.0, Rhs.1, Rhs.2 0 ~0% {4} | JOIN WITH num#DataFlowDispatch::CallTypeClassMethod#3508c3e5 ON FIRST 1 OUTPUT Lhs.3, Lhs.2, Lhs.0, Lhs.1 0 ~0% {4} | JOIN WITH `DataFlowDispatch::resolveCall/3#454c02d8#reorder_1_0_2#prev` ON FIRST 3 OUTPUT Lhs.1, Lhs.3, Lhs.0, Lhs.2 0 ~0% {5} | JOIN WITH num#DataFlowPublic::TCfgNode#2cd2fb22 ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0, Lhs.2, Lhs.3 0 ~0% {5} | JOIN WITH `DataFlowPublic::Node.getEnclosingCallable/0#dispred#be95825a` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.2, Lhs.3, Lhs.4 0 ~0% {4} | JOIN WITH `DataFlowPublic::Node.getEnclosingCallable/0#dispred#be95825a` ON FIRST 2 OUTPUT Lhs.0, Lhs.2, Lhs.3, Lhs.4 0 ~0% {5} | JOIN WITH num#DataFlowDispatch::TSelfArgumentPosition#de6d64b8 CARTESIAN PRODUCT OUTPUT Lhs.1, Lhs.2, Lhs.3, Lhs.0, Rhs.0 ``` Overall stats (old) Pipeline standard for DataFlowDispatch::getCallArg/5#21589076@b30c7vxg was evaluated in 51 iterations totaling 54ms (delta sizes total: 38247). ==> (new) Pipeline standard for DataFlowDispatch::getCallArg/5#21589076@c1559vxu was evaluated in 51 iterations totaling 28ms (delta sizes total: 38247). --- .../dataflow/new/internal/DataFlowDispatch.qll | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll index b869bdda521..d4af28d4e26 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -1302,9 +1302,7 @@ predicate getCallArg(CallNode call, Function target, CallType type, Node arg, Ar // // call_func(my_obj.some_method) // ``` - exists(CfgNode cfgNode | cfgNode.getNode() = call | - cfgNode.getEnclosingCallable() = arg.getEnclosingCallable() - ) + exists(CfgNode cfgNode | cfgNode.getNode() = call | sameEnclosingCallable(cfgNode, arg)) or // cls argument for classmethod calls -- see note above about bound methods type instanceof CallTypeClassMethod and @@ -1312,9 +1310,7 @@ predicate getCallArg(CallNode call, Function target, CallType type, Node arg, Ar resolveMethodCall(call, target, type, arg) and (arg = classTracker(_) or arg = clsArgumentTracker(_)) and // dataflow lib has requirement that arguments and calls are in same enclosing callable. - exists(CfgNode cfgNode | cfgNode.getNode() = call | - cfgNode.getEnclosingCallable() = arg.getEnclosingCallable() - ) + exists(CfgNode cfgNode | cfgNode.getNode() = call | sameEnclosingCallable(cfgNode, arg)) or // normal arguments for method calls ( @@ -1365,6 +1361,16 @@ predicate getCallArg(CallNode call, Function target, CallType type, Node arg, Ar ) } +/** + * join-order helper for getCallArg, since otherwise we would do cartesian product of + * the enclosing callables + */ +bindingset[node1, node2] +pragma[inline_late] +private predicate sameEnclosingCallable(Node node1, Node node2) { + node1.getEnclosingCallable() = node2.getEnclosingCallable() +} + // ============================================================================= // DataFlowCall // =============================================================================