diff --git a/WALA b/WALA index c0e260a62..7355e037f 160000 --- a/WALA +++ b/WALA @@ -1 +1 @@ -Subproject commit c0e260a62b9faa523bd907a747f9b83b90f090da +Subproject commit 7355e037f37486b38b019185baad9e1600d754be diff --git a/core/com.ibm.wala.cast.python.test/data/match1.py b/core/com.ibm.wala.cast.python.test/data/match1.py index 816d12130..679ddcc5d 100644 --- a/core/com.ibm.wala.cast.python.test/data/match1.py +++ b/core/com.ibm.wala.cast.python.test/data/match1.py @@ -26,20 +26,25 @@ def sunday(): print("Sunday") -day = 4 - -match day: - case 1: - monday() - case 2: - tuesday() - case 3: - wednesday() - case 4: - thursday() - case 5: - friday() - case 6: - saturday() - case 7: - sunday() +def otherDay(): + print("other day??") + + +for day in [1, 2, 3, 4, 5, 6, 7, otherDay]: + match day: + case 1: + monday() + case 2: + tuesday() + case 3: + wednesday() + case 4: + thursday() + case 5: + friday() + case 6: + saturday() + case 7: + sunday() + case x: + x() diff --git a/core/com.ibm.wala.cast.python.test/data/match2.py b/core/com.ibm.wala.cast.python.test/data/match2.py new file mode 100644 index 000000000..8c51b7991 --- /dev/null +++ b/core/com.ibm.wala.cast.python.test/data/match2.py @@ -0,0 +1,55 @@ +def monday(): + print("Monday") + + +def tuesday(): + print("Tuesday") + + +def wednesday(): + print("Wednesday") + + +def thursday(): + print("Thursday") + + +def friday(): + print("Friday") + + +def saturday(): + print("Saturday") + + +def sunday(): + print("Sunday") + + +def otherDay(): + print("other day??") + + +def doit(month): + for day in [1, 2, 3, 4, 5, 6, 7, otherDay]: + match day: + case 1 if 1 <= month <= 12: + monday() + case 2 if 1 <= month <= 12: + tuesday() + case 3 if 1 <= month <= 12: + wednesday() + case 4 if 1 <= month <= 12: + thursday() + case 5 if 1 <= month <= 12: + friday() + case 6 if 1 <= month <= 12: + saturday() + case 7 if 1 <= month <= 12: + sunday() + case x if callable(x): + x() + + +doit(4) +doit(0) diff --git a/core/com.ibm.wala.cast.python.test/data/match3.py b/core/com.ibm.wala.cast.python.test/data/match3.py new file mode 100644 index 000000000..3344e40f4 --- /dev/null +++ b/core/com.ibm.wala.cast.python.test/data/match3.py @@ -0,0 +1,31 @@ +def weekday(day): + print("week day " + str(day)) + + +def weekend(day): + print("week end " + str(day)) + + +def otherDay(): + print("other day??") + + +def somethingElse(): + print("something else") + + +def doit(month): + for day in [1, 2, 3, 4, 5, 6, 7, otherDay]: + match day: + case 1 | 2 | 3 | 4 | 5 as day if 1 <= month <= 12: + weekday(day) + case 6 | 7 as day if 1 <= month <= 12: + weekend(day) + case x if callable(x): + x() + case _: + somethingElse() + + +doit(4) +doit(0) diff --git a/core/com.ibm.wala.cast.python.test/data/match4.py b/core/com.ibm.wala.cast.python.test/data/match4.py new file mode 100644 index 000000000..8af5fe130 --- /dev/null +++ b/core/com.ibm.wala.cast.python.test/data/match4.py @@ -0,0 +1,41 @@ +def weekday(day): + print("week day " + str(day)) + + +def weekend(day): + print("week end " + str(day)) + + +def otherDay(): + print("other day??") + + +def somethingElse(): + print("something else") + + +def mten(): + return 10 + + +def mseven(): + return 10 + + +def doit(dmf): + match dmf: + case [1 | 2 | 3 | 4 | 5 as day, month] if 1 <= month() <= 12: + weekday(day) + case [6 | 7 as day, month] if 1 <= month() <= 12: + weekend(day) + case x if callable(x): + x() + case _: + somethingElse() + + +doit((3, mseven)) +doit([7, mten]) +doit([8, mten]) +doit(otherDay) +doit(0) diff --git a/core/com.ibm.wala.cast.python.test/data/match5.py b/core/com.ibm.wala.cast.python.test/data/match5.py new file mode 100644 index 000000000..7ab1f2048 --- /dev/null +++ b/core/com.ibm.wala.cast.python.test/data/match5.py @@ -0,0 +1,37 @@ +class Nothing: + def act(self): + print("Nothing") + + +def id(x): + return x + + +class Something: + a = 0 + b = 0 + + def __init__(self, a, b): + self.a = id(a) + self.b = id(b) + + def act(self): + print(self.a + self.b) + + +def doit(x): + match x: + case Nothing(): + x.act() + case Something(a=5, b=7): + x.act() + case Something(): + print("unexpected something") + case _: + print("unexpected value") + + +doit(Nothing()) +doit(Something(5, 7)) +doit(Something(3, 4)) +doit(10) diff --git a/core/com.ibm.wala.cast.python.test/source/com/ibm/wala/cast/python/test/TestPythonCallGraphShape.java b/core/com.ibm.wala.cast.python.test/source/com/ibm/wala/cast/python/test/TestPythonCallGraphShape.java index 352fc5475..fba4a0840 100644 --- a/core/com.ibm.wala.cast.python.test/source/com/ibm/wala/cast/python/test/TestPythonCallGraphShape.java +++ b/core/com.ibm.wala.cast.python.test/source/com/ibm/wala/cast/python/test/TestPythonCallGraphShape.java @@ -24,6 +24,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; @@ -130,4 +131,12 @@ StringBuffer dump(CallGraph CG) { } return sb; } + + protected void verifyGraphAssertions(CallGraph CG, Object[][] data) { + List asserts = new ArrayList<>(); + for (int i = 0; i < data.length; i++) { + asserts.add(new GraphAssertion(data[i][0], (String[]) data[i][1])); + } + verifyGraphAssertions(CG, asserts); + } } diff --git a/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/ir/PythonCAstToIRTranslator.java b/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/ir/PythonCAstToIRTranslator.java index 295346f0a..f5b2071d5 100644 --- a/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/ir/PythonCAstToIRTranslator.java +++ b/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/ir/PythonCAstToIRTranslator.java @@ -20,6 +20,7 @@ import com.ibm.wala.cast.ir.ssa.AstGlobalRead; import com.ibm.wala.cast.ir.ssa.AstInstructionFactory; import com.ibm.wala.cast.ir.translator.AstTranslator; +import com.ibm.wala.cast.ir.translator.AstTranslator.WalkContext; import com.ibm.wala.cast.loader.AstMethod.DebuggingInformation; import com.ibm.wala.cast.loader.DynamicCallSiteReference; import com.ibm.wala.cast.python.loader.DynamicAnnotatableEntity; @@ -1232,4 +1233,28 @@ protected void leaveBlockStmt(CAstNode n, WalkContext c, CAstVisitor visitor) { + int result = context.currentScope().allocateTempValue(); + context.setValue(n, result); + return false; + } + + @Override + protected void leaveInstanceOf( + CAstNode n, WalkContext context, CAstVisitor visitor) { + int result = context.getValue(n); + TypeReference ref = (TypeReference) n.getChild(0).getValue(); + + context + .cfg() + .addInstruction( + insts.InstanceofInstruction( + context.cfg().getCurrentInstruction(), + result, + context.getValue(n.getChild(1)), + ref)); + } } diff --git a/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/loader/PythonLoader.java b/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/loader/PythonLoader.java index d48c122c6..042d8cb7b 100644 --- a/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/loader/PythonLoader.java +++ b/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/loader/PythonLoader.java @@ -228,14 +228,16 @@ protected TranslatorToIR initTranslator(Set> topLe PythonTypes.comprehension.getName(), PythonTypes.CodeBody.getName(), this, null); final CoreClass object = new CoreClass(PythonTypes.object.getName(), PythonTypes.rootTypeName, this, null); + final CoreClass sequence = + new CoreClass(PythonTypes.sequence.getName(), PythonTypes.Root.getName(), this, null); final CoreClass list = - new CoreClass(PythonTypes.list.getName(), PythonTypes.Root.getName(), this, null); + new CoreClass(PythonTypes.list.getName(), PythonTypes.sequence.getName(), this, null); final CoreClass set = new CoreClass(PythonTypes.set.getName(), PythonTypes.Root.getName(), this, null); final CoreClass dict = new CoreClass(PythonTypes.dict.getName(), PythonTypes.Root.getName(), this, null); final CoreClass tuple = - new CoreClass(PythonTypes.tuple.getName(), PythonTypes.Root.getName(), this, null); + new CoreClass(PythonTypes.tuple.getName(), PythonTypes.sequence.getName(), this, null); final CoreClass string = new CoreClass(PythonTypes.string.getName(), PythonTypes.Root.getName(), this, null); final CoreClass trampoline = diff --git a/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/types/PythonTypes.java b/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/types/PythonTypes.java index 2baf404f0..076221881 100644 --- a/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/types/PythonTypes.java +++ b/core/com.ibm.wala.cast.python/source/com/ibm/wala/cast/python/types/PythonTypes.java @@ -103,6 +103,9 @@ public class PythonTypes extends AstTypeReference { public static final TypeReference coroutine = TypeReference.findOrCreate(pythonLoader, TypeName.findOrCreate("Lcoroutine")); + public static final TypeReference sequence = + TypeReference.findOrCreate(pythonLoader, TypeName.findOrCreate("Lsequence")); + /** https://docs.python.org/3/library/stdtypes.html#typeiter. */ public static final TypeReference iterator = TypeReference.findOrCreate(pythonLoader, TypeName.findOrCreate("Literator")); diff --git a/jep/com.ibm.wala.cast.python.cpython/.launchers/PassingSuite.launch b/jep/com.ibm.wala.cast.python.cpython/.launchers/PassingSuite.launch index 5e5a4a6bc..671f91348 100644 --- a/jep/com.ibm.wala.cast.python.cpython/.launchers/PassingSuite.launch +++ b/jep/com.ibm.wala.cast.python.cpython/.launchers/PassingSuite.launch @@ -7,6 +7,7 @@ + @@ -31,5 +32,5 @@ - + diff --git a/jep/com.ibm.wala.cast.python.cpython/source/com/ibm/wala/cast/python/jep/ast/CPythonAstToCAstTranslator.java b/jep/com.ibm.wala.cast.python.cpython/source/com/ibm/wala/cast/python/jep/ast/CPythonAstToCAstTranslator.java index 0cee0feeb..7f679a264 100644 --- a/jep/com.ibm.wala.cast.python.cpython/source/com/ibm/wala/cast/python/jep/ast/CPythonAstToCAstTranslator.java +++ b/jep/com.ibm.wala.cast.python.cpython/source/com/ibm/wala/cast/python/jep/ast/CPythonAstToCAstTranslator.java @@ -13,6 +13,7 @@ import com.ibm.wala.cast.python.ir.PythonCAstToIRTranslator; import com.ibm.wala.cast.python.ir.PythonLanguage; import com.ibm.wala.cast.python.jep.Util; +import com.ibm.wala.cast.python.jep.ast.CPythonAstToCAstTranslator.WalkContext; import com.ibm.wala.cast.python.loader.JepPythonLoaderFactory; import com.ibm.wala.cast.python.loader.PythonLoaderFactory; import com.ibm.wala.cast.python.parser.AbstractParser; @@ -404,6 +405,10 @@ default List inits() { default CAstNode matchVar() { return getParent().matchVar(); } + + default Set matchDeclNames() { + return getParent().matchDeclNames(); + } } private abstract static class Scope { @@ -484,6 +489,11 @@ public WalkContext codeParent() { public boolean isAsync() { return async; } + + @Override + public Set matchDeclNames() { + return Collections.emptySet(); + } } public abstract static class TranslationVisitor extends AbstractParser.CAstVisitor @@ -1426,8 +1436,10 @@ public CAstNode visitCompare(PyObject cmp, WalkContext context) { CAstNode op = translateOperator( ops.next().getAttr("__class__", PyObject.class).getAttr("__name__", String.class)); - CAstNode rhs = visit(exprs.next(), context); + PyObject exp = exprs.next(); + CAstNode rhs = visit(exp, context); CAstNode cmpop = ast.makeNode(CAstNode.BINARY_EXPR, op, ln, rhs); + ln = visit(exp, context); expr = expr == null ? cmpop @@ -2363,16 +2375,138 @@ public CAstNode visitMatchValue(PyObject match, WalkContext context) { visit(match.getAttr("value", PyObject.class), context)); } + public CAstNode visitMatchAs(PyObject match, WalkContext context) { + String id = match.getAttr("name", String.class); + PyObject pattern = match.getAttr("pattern", PyObject.class); + if (id == null) { + return ast.makeConstant(true); + } else if (pattern != null) { + context.matchDeclNames().add(id); + return ast.makeNode( + CAstNode.IF_EXPR, + visit(pattern, context), + ast.makeNode( + CAstNode.BLOCK_EXPR, + ast.makeNode( + CAstNode.ASSIGN, + ast.makeNode(CAstNode.VAR, ast.makeConstant(id)), + context.matchVar()), + ast.makeConstant(true)), + ast.makeConstant(false)); + } else { + context.matchDeclNames().add(id); + return ast.makeNode( + CAstNode.BLOCK_EXPR, + ast.makeNode( + CAstNode.ASSIGN, + ast.makeNode(CAstNode.VAR, ast.makeConstant(id)), + context.matchVar()), + ast.makeConstant(true)); + } + } + + public CAstNode visitMatchOr(PyObject match, WalkContext context) { + @SuppressWarnings("unchecked") + java.util.List patterns = match.getAttr("patterns", List.class); + return patterns.stream() + .map(o -> visit(o, context)) + .reduce( + (l, rhs) -> { + CAstNode lhs = + ast.makeNode( + CAstNode.DECL_STMT, + ast.makeConstant(new CAstSymbolImpl("__lhs__", CAstType.DYNAMIC)), + l); + return ast.makeNode( + CAstNode.BLOCK_EXPR, + lhs, + ast.makeNode( + CAstNode.IF_EXPR, + ast.makeNode(CAstNode.VAR, ast.makeConstant("__lhs__")), + ast.makeNode(CAstNode.VAR, ast.makeConstant("__lhs__")), + rhs)); + }) + .get(); + } + + public CAstNode visitMatchSequence(PyObject seq, WalkContext context) { + @SuppressWarnings("unchecked") + java.util.List patterns = seq.getAttr("patterns", List.class); + int i = 0; + CAstNode result = + ast.makeNode( + CAstNode.INSTANCEOF, ast.makeConstant(PythonTypes.sequence), context.matchVar()); + for (PyObject p : patterns) { + int idx = i; + WalkContext eltContext = + new WalkContext() { + @Override + public WalkContext getParent() { + return context; + } + + @Override + public CAstNode matchVar() { + return ast.makeNode(CAstNode.OBJECT_REF, context.matchVar(), ast.makeConstant(idx)); + } + }; + + CAstNode eltNode = visit(p, eltContext); + + result = ast.makeNode(CAstNode.IF_EXPR, result, eltNode, ast.makeConstant(false)); + i++; + } + + return result; + } + + public CAstNode visitMatchClass(PyObject cls, WalkContext context) { + PyObject klass = cls.getAttr("cls", PyObject.class); + CAstNode result = + ast.makeNode(CAstNode.INSTANCEOF, visit(klass, context), context.matchVar()); + + @SuppressWarnings("unchecked") + Iterator names = cls.getAttr("kwd_attrs", List.class).iterator(); + @SuppressWarnings("unchecked") + Iterator patterns = cls.getAttr("kwd_patterns", List.class).iterator(); + while (names.hasNext()) { + String name = names.next(); + WalkContext eltContext = + new WalkContext() { + + @Override + public WalkContext getParent() { + return context; + } + + @Override + public CAstNode matchVar() { + return ast.makeNode( + CAstNode.OBJECT_REF, context.matchVar(), ast.makeConstant(name)); + } + }; + + CAstNode eltNode = visit(patterns.next(), eltContext); + + result = ast.makeNode(CAstNode.IF_EXPR, result, eltNode, ast.makeConstant(false)); + } + + return result; + } + + private int nextDecl = 0; + public CAstNode visitMatch(PyObject match, WalkContext context) { - CAstNode var; + String exprVarName = "__expr" + nextDecl++ + "__"; CAstNode exprDecl = ast.makeNode( CAstNode.DECL_STMT, - var = ast.makeConstant(new CAstSymbolImpl("__expr__", CAstType.DYNAMIC)), + ast.makeConstant(new CAstSymbolImpl(exprVarName, CAstType.DYNAMIC)), visit(match.getAttr("subject", PyObject.class), context)); @SuppressWarnings("unchecked") java.util.List cases = match.getAttr("cases", List.class); + Set decls = HashSetFactory.make(); WalkContext mc = new WalkContext() { @@ -2383,7 +2517,12 @@ public WalkContext getParent() { @Override public CAstNode matchVar() { - return var; + return ast.makeNode(CAstNode.VAR, ast.makeConstant(exprVarName)); + } + + @Override + public Set matchDeclNames() { + return decls; } }; @@ -2415,6 +2554,22 @@ public CAstNode matchVar() { }) .collect(Collectors.toList())); + if (!decls.isEmpty()) { + exprDecl = + ast.makeNode( + CAstNode.BLOCK_STMT, + exprDecl, + ast.makeNode( + CAstNode.BLOCK_STMT, + decls.stream() + .map( + s -> + ast.makeNode( + CAstNode.DECL_STMT, + ast.makeConstant(new CAstSymbolImpl(s, CAstType.DYNAMIC)))) + .collect(Collectors.toList()))); + } + return ast.makeNode(CAstNode.BLOCK_STMT, exprDecl, body); } } diff --git a/jep/com.ibm.wala.cast.python.cpython/test/com/ibm/wala/cast/python/cpython/test/TestMatch.java b/jep/com.ibm.wala.cast.python.cpython/test/com/ibm/wala/cast/python/cpython/test/TestMatch.java index 3b3ec12cb..c1eff2380 100644 --- a/jep/com.ibm.wala.cast.python.cpython/test/com/ibm/wala/cast/python/cpython/test/TestMatch.java +++ b/jep/com.ibm.wala.cast.python.cpython/test/com/ibm/wala/cast/python/cpython/test/TestMatch.java @@ -1,9 +1,11 @@ package com.ibm.wala.cast.python.cpython.test; +import com.ibm.wala.cast.ipa.callgraph.CAstCallGraphUtil; import com.ibm.wala.cast.python.client.PythonAnalysisEngine; import com.ibm.wala.cast.python.ipa.callgraph.PythonSSAPropagationCallGraphBuilder; import com.ibm.wala.cast.python.test.TestJythonCallGraphShape; import com.ibm.wala.ipa.callgraph.CallGraph; +import com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter; import com.ibm.wala.ipa.cha.ClassHierarchyException; import com.ibm.wala.util.CancelException; import java.io.IOException; @@ -22,7 +24,8 @@ public class TestMatch extends TestJythonCallGraphShape { "script match1.py/thursday", "script match1.py/friday", "script match1.py/saturday", - "script match1.py/sunday" + "script match1.py/sunday", + "script match1.py/otherDay" } } }; @@ -33,6 +36,143 @@ public void testMatch1() PythonAnalysisEngine E = makeEngine("match1.py"); PythonSSAPropagationCallGraphBuilder B = E.defaultCallGraphBuilder(); CallGraph CG = B.makeCallGraph(B.getOptions()); + + System.err.println(CG); + CAstCallGraphUtil.AVOID_DUMP.set(false); + CAstCallGraphUtil.dumpCG( + (SSAContextInterpreter) B.getContextInterpreter(), B.getPointerAnalysis(), CG); + verifyGraphAssertions(CG, assertionsForMatch1); } + + protected static final Object[][] assertionsForMatch2 = + new Object[][] { + new Object[] {ROOT, new String[] {"script match2.py"}}, + new Object[] {"script match2.py", new String[] {"script match2.py/doit"}}, + new Object[] { + "script match2.py/doit", + new String[] { + "script match2.py/monday", + "script match2.py/tuesday", + "script match2.py/wednesday", + "script match2.py/thursday", + "script match2.py/friday", + "script match2.py/saturday", + "script match2.py/sunday", + "script match2.py/otherDay" + } + } + }; + + @Test + public void testMatch2() + throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException { + PythonAnalysisEngine E = makeEngine("match2.py"); + PythonSSAPropagationCallGraphBuilder B = E.defaultCallGraphBuilder(); + CallGraph CG = B.makeCallGraph(B.getOptions()); + + System.err.println(CG); + CAstCallGraphUtil.AVOID_DUMP.set(false); + CAstCallGraphUtil.dumpCG( + (SSAContextInterpreter) B.getContextInterpreter(), B.getPointerAnalysis(), CG); + + verifyGraphAssertions(CG, assertionsForMatch2); + } + + protected static final Object[][] assertionsForMatch3 = + new Object[][] { + new Object[] {ROOT, new String[] {"script match3.py"}}, + new Object[] {"script match3.py", new String[] {"script match3.py/doit"}}, + new Object[] { + "script match3.py/doit", + new String[] { + "script match3.py/weekday", + "script match3.py/weekend", + "script match3.py/otherDay", + "script match3.py/somethingElse" + } + } + }; + + @Test + public void testMatch3() + throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException { + PythonAnalysisEngine E = makeEngine("match3.py"); + PythonSSAPropagationCallGraphBuilder B = E.defaultCallGraphBuilder(); + CallGraph CG = B.makeCallGraph(B.getOptions()); + + System.err.println(CG); + CAstCallGraphUtil.AVOID_DUMP.set(false); + CAstCallGraphUtil.dumpCG( + (SSAContextInterpreter) B.getContextInterpreter(), B.getPointerAnalysis(), CG); + + verifyGraphAssertions(CG, assertionsForMatch3); + } + + protected static final Object[][] assertionsForMatch4 = + new Object[][] { + new Object[] {ROOT, new String[] {"script match4.py"}}, + new Object[] {"script match4.py", new String[] {"script match4.py/doit"}}, + new Object[] { + "script match4.py/doit", + new String[] { + "script match4.py/mseven", + "script match4.py/mten", + "script match4.py/weekday", + "script match4.py/weekend", + "script match4.py/otherDay", + "script match4.py/somethingElse" + } + } + }; + + @Test + public void testMatch4() + throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException { + PythonAnalysisEngine E = makeEngine("match4.py"); + PythonSSAPropagationCallGraphBuilder B = E.defaultCallGraphBuilder(); + CallGraph CG = B.makeCallGraph(B.getOptions()); + + System.err.println(CG); + CAstCallGraphUtil.AVOID_DUMP.set(false); + CAstCallGraphUtil.dumpCG( + (SSAContextInterpreter) B.getContextInterpreter(), B.getPointerAnalysis(), CG); + + verifyGraphAssertions(CG, assertionsForMatch4); + } + + protected static final Object[][] assertionsForMatch5 = + new Object[][] { + new Object[] {ROOT, new String[] {"script match5.py"}}, + new Object[] {"script match5.py", new String[] {"script match5.py/doit"}}, + new Object[] { + "script match5.py/doit", + new String[] { + "$script match5.py/Nothing/act:trampoline1", + "$script match5.py/Something/act:trampoline1", + } + }, + new Object[] { + "$script match5.py/Nothing/act:trampoline1", new String[] {"script match5.py/Nothing/act"} + }, + new Object[] { + "$script match5.py/Something/act:trampoline1", + new String[] {"script match5.py/Something/act"} + } + }; + + @Test + public void testMatch5() + throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException { + PythonAnalysisEngine E = makeEngine("match5.py"); + PythonSSAPropagationCallGraphBuilder B = E.defaultCallGraphBuilder(); + CallGraph CG = B.makeCallGraph(B.getOptions()); + + System.err.println(CG); + CAstCallGraphUtil.AVOID_DUMP.set(false); + CAstCallGraphUtil.dumpCG( + (SSAContextInterpreter) B.getContextInterpreter(), B.getPointerAnalysis(), CG); + + verifyGraphAssertions(CG, assertionsForMatch5); + } }