From cf044e29295e5c748c12c9983e73301b00e93332 Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Mon, 20 Apr 2026 15:06:01 -0700 Subject: [PATCH 01/18] Test --- YOOO.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 YOOO.txt diff --git a/YOOO.txt b/YOOO.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/YOOO.txt @@ -0,0 +1 @@ + From 63dfd2bb6a997dea35be81f6b3f1b97da421ef35 Mon Sep 17 00:00:00 2001 From: jag2289-blip Date: Mon, 20 Apr 2026 15:06:08 -0700 Subject: [PATCH 02/18] Create hello.txt --- hello.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 hello.txt diff --git a/hello.txt b/hello.txt new file mode 100644 index 0000000..f2aa86d --- /dev/null +++ b/hello.txt @@ -0,0 +1 @@ +hello From b978358c52efd5f7e9a2b20ce29d8e982a9a514a Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Mon, 20 Apr 2026 15:07:14 -0700 Subject: [PATCH 03/18] Test Completed --- YOOO.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 YOOO.txt diff --git a/YOOO.txt b/YOOO.txt deleted file mode 100644 index 8b13789..0000000 --- a/YOOO.txt +++ /dev/null @@ -1 +0,0 @@ - From d9e8f82d471820711bd70cce2aec0c2fcebe66cf Mon Sep 17 00:00:00 2001 From: jag2289-blip Date: Mon, 20 Apr 2026 15:08:03 -0700 Subject: [PATCH 04/18] Delete hello.txt --- hello.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 hello.txt diff --git a/hello.txt b/hello.txt deleted file mode 100644 index f2aa86d..0000000 --- a/hello.txt +++ /dev/null @@ -1 +0,0 @@ -hello From 7bebbaa2db7c1e5e139852a2c9d17a1097a7178a Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Wed, 22 Apr 2026 13:39:17 -0700 Subject: [PATCH 05/18] Add test file for comment format in .rules files A placeholder file to later be edited with tests --- tests/test_comments_rules.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/test_comments_rules.py diff --git a/tests/test_comments_rules.py b/tests/test_comments_rules.py new file mode 100644 index 0000000..23ed092 --- /dev/null +++ b/tests/test_comments_rules.py @@ -0,0 +1 @@ +# This file is to test comment format for .rules files as stated in (Issue 23) From c8b8cbbad14d2bc80f9f7a27abb81c6d414d71ba Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Thu, 23 Apr 2026 00:36:23 -0700 Subject: [PATCH 06/18] Possible fix for intergrating comments into .rules files and a template for unit testing with some tests to be added to later --- tests/test_comments_rules.py | 80 ++++++++++++++++++++++++++++++++++++ universalmutator/mutator.py | 41 +++++++++++------- 2 files changed, 107 insertions(+), 14 deletions(-) diff --git a/tests/test_comments_rules.py b/tests/test_comments_rules.py index 23ed092..b820c83 100644 --- a/tests/test_comments_rules.py +++ b/tests/test_comments_rules.py @@ -1 +1,81 @@ # This file is to test comment format for .rules files as stated in (Issue 23) +import unittest +import io +import sys +import tempfile +import os + +from universalmutator.mutator import parseRules + +# written tests, still not tested! + +class TestParseRulesComments(unittest.TestCase): + + def _parse(self, rule_text): + """ + Helper: write rules to temp file, run parseRules, capture stdout + """ + fd, path = tempfile.mkstemp(suffix=".rules") + os.close(fd) + + try: + with open(path, "w") as f: + f.write(rule_text) + + # capture print output + buffer = io.StringIO() + old_stdout = sys.stdout + sys.stdout = buffer + + try: + rules, ignoreRules, skipRules = parseRules([path]) + finally: + sys.stdout = old_stdout + + return rules, ignoreRules, skipRules, buffer.getvalue() + + finally: + os.remove(path) + + # --------------------------------------------------- + # TEST 1: indented comments should be ignored + # --------------------------------------------------- + def test_indented_comments_ignored(self): + rules, ignoreRules, skipRules, out = self._parse( + " # comment line\n" + "\t# tab comment\n" + "\\+ ==> -\n" + ) + + self.assertEqual(len(rules), 1) + self.assertEqual(len(ignoreRules), 0) + self.assertEqual(len(skipRules), 0) + + # --------------------------------------------------- + # TEST 2: blank lines should be ignored + # --------------------------------------------------- + def test_blank_lines_ignored(self): + rules, _, _, out = self._parse( + "\\+ ==> -\n" + "\n" + " \n" + "\\* ==> /\n" + ) + + self.assertEqual(len(rules), 2) + + # --------------------------------------------------- + # TEST 3: rules starting with '#' must NOT be treated as comments + # --------------------------------------------------- + def test_hash_rules_still_parse(self): + rules, ignoreRules, skipRules, out = self._parse( + "#include ==> DO_NOT_MUTATE\n" + "# ==> SKIP_MUTATING_REST\n" + ) + + self.assertEqual(len(ignoreRules), 1) + self.assertEqual(len(skipRules), 1) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/universalmutator/mutator.py b/universalmutator/mutator.py index 1791d61..f3c6730 100644 --- a/universalmutator/mutator.py +++ b/universalmutator/mutator.py @@ -37,22 +37,35 @@ def parseRules(ruleFiles, comby=False): for (r, ruleSource) in rulesText: ruleLineNo += 1 - if r == "\n": + + # remove all leading and trailing white space + line = r.strip() + + # check for blank lines + if line == "": + # ignore blank lines continue - if " ==> " not in r: - if " ==>" in r: - s = r.split(" ==>") - else: - if r[0] == "#": # Don't warn about comments - continue - print("*" * 60) - print("WARNING:") - print("RULE:", r, "FROM", ruleSource) - print("DOES NOT MATCH EXPECTED FORMAT, AND SO WAS IGNORED") - print("*" * 60) - continue # Allow blank lines and comments, just ignore lines without a transformation + + # handle comments + if (line.startswith("#") or line.startswith("--")) and "==>" not in line: + # ignore comments & DO NOT IGNORE # ==> SKIP_MUTATING_REST + continue + + # check and parse valid rules + if " ==> " in line: + s = line.split(" ==> ") + elif " ==>" in line: + s = line.split(" ==>") else: - s = r.split(" ==> ") + # otherwise it's a invalid line and warn user + print("*" * 60) + print("WARNING:") + print("RULE:", line, "FROM", ruleSource) + print("DOES NOT MATCH EXPECTED FORMAT, AND SO WAS IGNORED") + print("*" * 60) + continue + + # End of possible fix if comby: lhs = s[0] From 8d7d90e5ca35eb16b1d2181839afaf96e21a9a1a Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Thu, 23 Apr 2026 11:42:53 -0700 Subject: [PATCH 07/18] New commenting system that only uses # and # DISABLED: is a safe way to comment out rules --- universalmutator/mutator.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/universalmutator/mutator.py b/universalmutator/mutator.py index f3c6730..def4003 100644 --- a/universalmutator/mutator.py +++ b/universalmutator/mutator.py @@ -45,10 +45,15 @@ def parseRules(ruleFiles, comby=False): if line == "": # ignore blank lines continue - + # handle comments - if (line.startswith("#") or line.startswith("--")) and "==>" not in line: - # ignore comments & DO NOT IGNORE # ==> SKIP_MUTATING_REST + if line.startswith("#") and "==>" not in line: + # ignore comments '#' + continue + + # check for disabled rules + if line.startswith("# DISABLED:"): + # ignore disabled rules continue # check and parse valid rules From 13e8aa39e9433b2bc24e350dadc27bb44d681819 Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Thu, 23 Apr 2026 12:43:24 -0700 Subject: [PATCH 08/18] Updated Rule File --- tests/test_comments_rules.py | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_comments_rules.py b/tests/test_comments_rules.py index b820c83..33d5083 100644 --- a/tests/test_comments_rules.py +++ b/tests/test_comments_rules.py @@ -60,6 +60,7 @@ def test_blank_lines_ignored(self): "\n" " \n" "\\* ==> /\n" + "\t\n" ) self.assertEqual(len(rules), 2) @@ -76,6 +77,49 @@ def test_hash_rules_still_parse(self): self.assertEqual(len(ignoreRules), 1) self.assertEqual(len(skipRules), 1) + # --------------------------------------------------- + # TEST 4: only comments should result in no rules and no warnings + # --------------------------------------------------- + + def test_only_comments(self): + rules, ignoreRules, skipRules, out = self._parse( + "# comment\n" + " # indented comment\n" + " # spaced comment\n" + ) + + self.assertEqual(len(rules), 0) + self.assertEqual(len(ignoreRules), 0) + self.assertEqual(len(skipRules), 0) + + # --------------------------------------------------- + # TEST 5: Invalid lines should still warn, but not be treated as rules + # --------------------------------------------------- + + + # --------------------------------------------------- + # TEST 6: Mixed file test with comments, blank lines, valid rules, and invalid lines + # --------------------------------------------------- + + + # --------------------------------------------------- + # TEST 7: Disabled rules should be ignored, but still treated as comments + # --------------------------------------------------- + + + # --------------------------------------------------- + # TEST 8: Disabled rules with different spacing should still be ignored (MAYBE?) + # --------------------------------------------------- + + + # --------------------------------------------------- + # TEST 9: Larger file test with multiple comments, blank lines, valid rules, invalid lines, and disabled rules + # --------------------------------------------------- + + + # --------------------------------------------------- + # TEST 10: Header Testing with comments, blank lines before and after header, and example rules comments + # --------------------------------------------------- if __name__ == "__main__": unittest.main() \ No newline at end of file From a165e8d8f28ef5d9a26058ab06d770bdd693df09 Mon Sep 17 00:00:00 2001 From: jag2289-blip Date: Fri, 24 Apr 2026 00:54:59 +0000 Subject: [PATCH 09/18] Ok should work now --- build/lib/universalmutator/__init__.py | 0 build/lib/universalmutator/analyze.py | 335 ++++++++++ build/lib/universalmutator/c_handler.py | 4 + build/lib/universalmutator/checkcov.py | 77 +++ build/lib/universalmutator/comby/c.rules | 0 build/lib/universalmutator/comby/c_like.rules | 20 + build/lib/universalmutator/comby/cpp.rules | 14 + .../lib/universalmutator/comby/fortran.rules | 112 ++++ build/lib/universalmutator/comby/go.rules | 6 + build/lib/universalmutator/comby/java.rules | 10 + build/lib/universalmutator/comby/lisp.rules | 8 + build/lib/universalmutator/comby/none.rules | 0 build/lib/universalmutator/comby/python.rules | 47 ++ build/lib/universalmutator/comby/r.rules | 1 + build/lib/universalmutator/comby/rust.rules | 2 + .../lib/universalmutator/comby/solidity.rules | 44 ++ build/lib/universalmutator/comby/swift.rules | 13 + .../universalmutator/comby/universal.rules | 96 +++ build/lib/universalmutator/comby/vyper.rules | 0 build/lib/universalmutator/cpp_handler.py | 4 + build/lib/universalmutator/fe_handler.py | 45 ++ build/lib/universalmutator/findmissing.py | 54 ++ build/lib/universalmutator/fortran_handler.py | 4 + build/lib/universalmutator/genmutants.py | 623 ++++++++++++++++++ build/lib/universalmutator/go_handler.py | 4 + build/lib/universalmutator/intersect.py | 33 + build/lib/universalmutator/java_handler.py | 26 + .../universalmutator/javascript_handler.py | 4 + build/lib/universalmutator/lisp_handler.py | 4 + build/lib/universalmutator/mutator.py | 298 +++++++++ build/lib/universalmutator/prioritize.py | 131 ++++ build/lib/universalmutator/prune.py | 136 ++++ build/lib/universalmutator/python_handler.py | 85 +++ build/lib/universalmutator/r_handler.py | 4 + build/lib/universalmutator/rust_handler.py | 28 + build/lib/universalmutator/show.py | 71 ++ .../lib/universalmutator/solidity_handler.py | 59 ++ build/lib/universalmutator/static/c.rules | 0 .../lib/universalmutator/static/c_like.rules | 20 + build/lib/universalmutator/static/cpp.rules | 14 + build/lib/universalmutator/static/fe.rules | 0 .../lib/universalmutator/static/fortran.rules | 112 ++++ build/lib/universalmutator/static/go.rules | 6 + build/lib/universalmutator/static/java.rules | 12 + .../universalmutator/static/javascript.rules | 6 + build/lib/universalmutator/static/lisp.rules | 7 + build/lib/universalmutator/static/none.rules | 0 .../lib/universalmutator/static/python.rules | 43 ++ build/lib/universalmutator/static/r.rules | 1 + build/lib/universalmutator/static/rust.rules | 2 + .../universalmutator/static/solidity.rules | 50 ++ build/lib/universalmutator/static/swift.rules | 15 + .../universalmutator/static/universal.rules | 96 +++ build/lib/universalmutator/static/vyper.rules | 0 build/lib/universalmutator/swift_handler.py | 29 + build/lib/universalmutator/utils.py | 221 +++++++ build/lib/universalmutator/vyper_handler.py | 31 + universalmutator.egg-info/PKG-INFO | 152 +++++ universalmutator.egg-info/SOURCES.txt | 68 ++ .../dependency_links.txt | 1 + universalmutator.egg-info/entry_points.txt | 8 + universalmutator.egg-info/requires.txt | 3 + universalmutator.egg-info/top_level.txt | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 154 bytes .../__pycache__/c_handler.cpython-312.pyc | Bin 0 -> 328 bytes .../__pycache__/cpp_handler.cpython-312.pyc | Bin 0 -> 330 bytes .../__pycache__/fe_handler.cpython-312.pyc | Bin 0 -> 2564 bytes .../fortran_handler.cpython-312.pyc | Bin 0 -> 334 bytes .../__pycache__/genmutants.cpython-312.pyc | Bin 0 -> 23503 bytes .../__pycache__/go_handler.cpython-312.pyc | Bin 0 -> 329 bytes .../__pycache__/java_handler.cpython-312.pyc | Bin 0 -> 2053 bytes .../javascript_handler.cpython-312.pyc | Bin 0 -> 337 bytes .../__pycache__/lisp_handler.cpython-312.pyc | Bin 0 -> 331 bytes .../__pycache__/mutator.cpython-312.pyc | Bin 0 -> 11045 bytes .../python_handler.cpython-312.pyc | Bin 0 -> 3728 bytes .../__pycache__/r_handler.cpython-312.pyc | Bin 0 -> 328 bytes .../__pycache__/rust_handler.cpython-312.pyc | Bin 0 -> 1871 bytes .../solidity_handler.cpython-312.pyc | Bin 0 -> 3241 bytes .../__pycache__/swift_handler.cpython-312.pyc | Bin 0 -> 1877 bytes .../__pycache__/vyper_handler.cpython-312.pyc | Bin 0 -> 1932 bytes universalmutator/mutator.py | 4 +- 81 files changed, 3302 insertions(+), 2 deletions(-) create mode 100644 build/lib/universalmutator/__init__.py create mode 100644 build/lib/universalmutator/analyze.py create mode 100644 build/lib/universalmutator/c_handler.py create mode 100644 build/lib/universalmutator/checkcov.py create mode 100644 build/lib/universalmutator/comby/c.rules create mode 100644 build/lib/universalmutator/comby/c_like.rules create mode 100644 build/lib/universalmutator/comby/cpp.rules create mode 100644 build/lib/universalmutator/comby/fortran.rules create mode 100644 build/lib/universalmutator/comby/go.rules create mode 100644 build/lib/universalmutator/comby/java.rules create mode 100644 build/lib/universalmutator/comby/lisp.rules create mode 100644 build/lib/universalmutator/comby/none.rules create mode 100644 build/lib/universalmutator/comby/python.rules create mode 100644 build/lib/universalmutator/comby/r.rules create mode 100644 build/lib/universalmutator/comby/rust.rules create mode 100644 build/lib/universalmutator/comby/solidity.rules create mode 100644 build/lib/universalmutator/comby/swift.rules create mode 100644 build/lib/universalmutator/comby/universal.rules create mode 100644 build/lib/universalmutator/comby/vyper.rules create mode 100644 build/lib/universalmutator/cpp_handler.py create mode 100644 build/lib/universalmutator/fe_handler.py create mode 100644 build/lib/universalmutator/findmissing.py create mode 100644 build/lib/universalmutator/fortran_handler.py create mode 100644 build/lib/universalmutator/genmutants.py create mode 100644 build/lib/universalmutator/go_handler.py create mode 100644 build/lib/universalmutator/intersect.py create mode 100644 build/lib/universalmutator/java_handler.py create mode 100644 build/lib/universalmutator/javascript_handler.py create mode 100644 build/lib/universalmutator/lisp_handler.py create mode 100644 build/lib/universalmutator/mutator.py create mode 100644 build/lib/universalmutator/prioritize.py create mode 100644 build/lib/universalmutator/prune.py create mode 100644 build/lib/universalmutator/python_handler.py create mode 100644 build/lib/universalmutator/r_handler.py create mode 100644 build/lib/universalmutator/rust_handler.py create mode 100644 build/lib/universalmutator/show.py create mode 100644 build/lib/universalmutator/solidity_handler.py create mode 100644 build/lib/universalmutator/static/c.rules create mode 100644 build/lib/universalmutator/static/c_like.rules create mode 100644 build/lib/universalmutator/static/cpp.rules create mode 100644 build/lib/universalmutator/static/fe.rules create mode 100644 build/lib/universalmutator/static/fortran.rules create mode 100644 build/lib/universalmutator/static/go.rules create mode 100644 build/lib/universalmutator/static/java.rules create mode 100644 build/lib/universalmutator/static/javascript.rules create mode 100644 build/lib/universalmutator/static/lisp.rules create mode 100644 build/lib/universalmutator/static/none.rules create mode 100644 build/lib/universalmutator/static/python.rules create mode 100644 build/lib/universalmutator/static/r.rules create mode 100644 build/lib/universalmutator/static/rust.rules create mode 100644 build/lib/universalmutator/static/solidity.rules create mode 100644 build/lib/universalmutator/static/swift.rules create mode 100644 build/lib/universalmutator/static/universal.rules create mode 100644 build/lib/universalmutator/static/vyper.rules create mode 100644 build/lib/universalmutator/swift_handler.py create mode 100644 build/lib/universalmutator/utils.py create mode 100644 build/lib/universalmutator/vyper_handler.py create mode 100644 universalmutator.egg-info/PKG-INFO create mode 100644 universalmutator.egg-info/SOURCES.txt create mode 100644 universalmutator.egg-info/dependency_links.txt create mode 100644 universalmutator.egg-info/entry_points.txt create mode 100644 universalmutator.egg-info/requires.txt create mode 100644 universalmutator.egg-info/top_level.txt create mode 100644 universalmutator/__pycache__/__init__.cpython-312.pyc create mode 100644 universalmutator/__pycache__/c_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/cpp_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/fe_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/fortran_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/genmutants.cpython-312.pyc create mode 100644 universalmutator/__pycache__/go_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/java_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/javascript_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/lisp_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/mutator.cpython-312.pyc create mode 100644 universalmutator/__pycache__/python_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/r_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/rust_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/solidity_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/swift_handler.cpython-312.pyc create mode 100644 universalmutator/__pycache__/vyper_handler.cpython-312.pyc diff --git a/build/lib/universalmutator/__init__.py b/build/lib/universalmutator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/universalmutator/analyze.py b/build/lib/universalmutator/analyze.py new file mode 100644 index 0000000..d48450b --- /dev/null +++ b/build/lib/universalmutator/analyze.py @@ -0,0 +1,335 @@ +from __future__ import print_function + +import difflib +import subprocess +import sys +import platform +import glob +import shutil +import signal +import time +import random +import os +import py_compile + +def main(): + + isWindows = platform.system() + args = sys.argv + + if ("--help" in args) or (len(sys.argv) < 3): + if len(sys.argv) < 3: + print("ERROR: analyze_mutants requires at least two arguments\n") + print("USAGE: analyze_mutants [--mutantDir ] [--fromFile ]") + print(" is command to execute to run tests; non-zero return indicates mutant killed") + print(" --mutantDir: directory with all mutants; defaults to current directory") + print(" --fromFile: file containing list of mutants to process; others ignored") + print(" --timeout : change the timeout setting") + print(" --show: show mutants") + print(" --verbose: show mutants and output of analysis") + print(" --seed: random seed for shuffling of mutants") + print(" --noShuffle: do not randomize order of mutants") + print(" --resume: use existing killed.txt and notkilled.txt, resume mutation analysis") + print(" --prefix: add a prefix to killed.txt and notkilled.txt") + print(" --numMutants: run with specific number of mutants") + print(" --compileCommand: compile command to run in selecting mutants") + sys.exit(0) + + verbose = "--verbose" in sys.argv + if verbose: + args.remove("--verbose") + + showM = "--show" in sys.argv + if showM: + args.remove("--show") + + resume = "--resume" in sys.argv + if resume: + args.remove("--resume") + + noShuffle = "--noShuffle" in sys.argv + if noShuffle: + args.remove("--noShuffle") + + prefix = None + try: + prefixpos = args.index("--prefix") + except ValueError: + prefixpos = -1 + + if prefixpos != -1: + prefix = args[prefixpos + 1] + args.remove("--prefix") + args.remove(prefix) + + fromFile = None + try: + filepos = args.index("--fromFile") + except ValueError: + filepos = -1 + + if filepos != -1: + fromFile = args[filepos + 1] + args.remove("--fromFile") + args.remove(fromFile) + + seed = None + try: + seedpos = args.index("--seed") + except ValueError: + seedpos = -1 + + if seedpos != -1: + seed = args[seedpos + 1] + args.remove("--seed") + args.remove(seed) + seed = int(seed) + + timeout = 30 + try: + topos = args.index("--timeout") + except ValueError: + topos = -1 + + if topos != -1: + timeout = args[topos + 1] + args.remove("--timeout") + args.remove(timeout) + timeout = float(timeout) + + numMutants = -1 + try: + nmpos = args.index("--numMutants") + except ValueError: + nmpos = -1 + + if nmpos != -1: + numMutants = args[nmpos + 1] + args.remove("--numMutants") + args.remove(numMutants) + numMutants = int(numMutants) + + compileCommand = None + try: + ccmdpos = args.index("--compileCommand") + except ValueError: + ccmdpos = -1 + + if ccmdpos != -1: + compileCommand = args[ccmdpos + 1] + args.remove("--compileCommand") + args.remove(compileCommand) + + onlyMutants = None + if fromFile is not None: + with open(fromFile, 'r') as file: + onlyMutants = file.read().split() + + mdir = "." + try: + mdirpos = args.index("--mutantDir") + except ValueError: + mdirpos = -1 + + if mdirpos != -1: + mdir = args[mdirpos + 1] + args.remove("--mutantDir") + args.remove(mdir) + if mdir[-1] != "/": + mdir += "/" + + src = args[1] + tstCmd = [args[2]] + ignore = [] + if len(args) > 3: + with open(sys.argv[3]) as file: + for l in file: + ignore.append(l.split()[0]) + + srcBase = src.split("/")[-1] + srcEnd = "." + ((src.split(".")[-1]).split("/")[-1]) + + count = 0.0 + killCount = 0.0 + + killFileName = "killed.txt" + notkillFileName = "notkilled.txt" + if prefix is not None: + killFileName = prefix + "." + killFileName + notkillFileName = prefix + "." + notkillFileName + + print("ANALYZING", src) + print("COMMAND: **", tstCmd, "**") + + allTheMutants = glob.glob(mdir + srcBase.replace(srcEnd, ".mutant*" + srcEnd)) + + if onlyMutants is not None: + newMutants = [] + for f1 in onlyMutants: + for f2 in allTheMutants: + if f2.split("/")[-1] == f1: + newMutants.append(f2) + allTheMutants = newMutants + + if seed is not None: + random.seed(seed) + + if not noShuffle: + random.shuffle(allTheMutants) + + allStart = time.time() + + if resume: + alreadyKilled = [] + alreadyNotKilled = [] + if not (os.path.exists(killFileName) and os.path.exists(notkillFileName)): + print("ATTEMPTING TO RESUME, BUT NO PREVIOUS RESULTS FOUND") + else: + with open(killFileName, 'r') as killed: + with open(notkillFileName, 'r') as notkilled: + for line in killed: + if line == "\n": + continue + alreadyKilled.append(line[:-1]) + count += 1 + killCount += 1 + for line in notkilled: + if line == "\n": + continue + alreadyNotKilled.append(line[:-1]) + count += 1 + print("RESUMING FROM EXISTING RUN, WITH", int(killCount), "KILLED MUTANTS OUT OF", int(count)) + + # numMutants = -1 implies no --numMutants argument was provided + totalMutants = min(numMutants, len(allTheMutants)) if numMutants > 0 else len(allTheMutants) + + with open(os.devnull, 'w') as dnull: + with open(killFileName, 'w') as killed: + with open(notkillFileName, 'w') as notkilled: + if resume: + for line in alreadyKilled: + killed.write(line + "\n") + killed.flush() + for line in alreadyNotKilled: + notkilled.write(line + "\n") + notkilled.flush() + for f in allTheMutants: + if resume: + if (f.split("/")[-1] in alreadyKilled) or (f.split("/")[-1] in alreadyNotKilled): + continue + if f in ignore: + print(f, "SKIPPED") + if numMutants != -1 and compileCommand is not None: + if runCmd(compileCommand, src, f) != "VALID": + continue + + print("=" * 80) + print("#" + str(int(count) + 1) + ":", end=" ") + print("[" + str(round(time.time() - allStart, 2)) + "s", end=" ") + print(str(round(count / totalMutants * 100.0, 2)) + "% DONE]") + if verbose or showM: + print("MUTANT:", f) + with open(src, 'r') as ff: + fromLines = ff.readlines() + with open(f, 'r') as tf: + toLines = tf.readlines() + diff = difflib.context_diff(fromLines, toLines, "Original", "Mutant") + print(''.join(diff)) + print() + sys.stdout.flush() + print("RUNNING", f + "...") + sys.stdout.flush() + try: + shutil.copy(src, src + ".um.backup") + shutil.copy(f, src) + if srcEnd == ".py": + py_compile.compile(src) + + if isWindows: + ctstCmd = ['set "CURRENT_MUTANT_SOURCE=' + f + '" && ' + tstCmd[0]] + else: + ctstCmd = ['export CURRENT_MUTANT_SOURCE="' + f + '"; ' + tstCmd[0]] + start = time.time() + + if not verbose: + if isWindows: + P = subprocess.Popen(ctstCmd, shell=True, stderr=dnull, stdout=dnull, + start_new_session=True) + else: + P = subprocess.Popen(ctstCmd, shell=True, stderr=dnull, stdout=dnull, + preexec_fn=os.setsid) + else: + if isWindows: + P = subprocess.Popen(ctstCmd, shell=True, start_new_session=True) + else: + P = subprocess.Popen(ctstCmd, shell=True, preexec_fn=os.setsid) + + try: + while P.poll() is None and (time.time() - start) < timeout: + time.sleep(0.05) + finally: + if P.poll() is None: + print() + print("HAD TO TERMINATE ANALYSIS (TIMEOUT OR EXCEPTION)") + + if isWindows: + os.kill(P.pid, signal.SIGTERM) + else: + os.killpg(os.getpgid(P.pid), signal.SIGTERM) + + # Avoid any weird race conditions from grabbing the return code + time.sleep(0.05) + r = P.returncode + + runtime = time.time() - start + + count += 1 + if numMutants != -1 and count >= numMutants: + break + if r == 0: + print(f, "NOT KILLED") + notkilled.write(f.split("/")[-1] + "\n") + notkilled.flush() + else: + killCount += 1 + print(f, "KILLED IN", runtime, "(RETURN CODE", str(r) + ")") + killed.write(f.split("/")[-1] + "\n") + killed.flush() + print(" RUNNING SCORE:", killCount / count) + sys.stdout.flush() + finally: + shutil.copy(src + ".um.backup", src) + os.remove(src + ".um.backup") + if os.path.exists(".um.mutant_output." + str(os.getpid())): + os.remove(".um.mutant_output." + str(os.getpid())) + + print("=" * 80) + + if count == 0: + print("!!! No valid mutants found! Make sure you specified the right mutant directory !!!") + return + print("MUTATION SCORE:", killCount / count) + + +def runCmd(cmd, sourceFile, mutantFile): + if "MUTANT" not in cmd: + # We asssume if the MUTANT isn't part of the command, + # we need to move it into place, before, e.g., make + backupName = sourceFile + ".um.backup." + str(os.getpid()) + shutil.copy(sourceFile, backupName) + shutil.copy(mutantFile, sourceFile) + try: + with open(".um.mutant_output." + str(os.getpid()), 'w') as file: + r = subprocess.call([cmd.replace("MUTANT", mutantFile)], + shell=True, stderr=file, stdout=file) + if r == 0: + return "VALID" + return "INVALID" + finally: + # If we moved the mutant in, restore original + if "MUTANT" not in cmd: + shutil.copy(backupName, sourceFile) + + +if __name__ == '__main__': + main() diff --git a/build/lib/universalmutator/c_handler.py b/build/lib/universalmutator/c_handler.py new file mode 100644 index 0000000..2222de5 --- /dev/null +++ b/build/lib/universalmutator/c_handler.py @@ -0,0 +1,4 @@ +dumb = True + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + return "VALID" diff --git a/build/lib/universalmutator/checkcov.py b/build/lib/universalmutator/checkcov.py new file mode 100644 index 0000000..adbf48e --- /dev/null +++ b/build/lib/universalmutator/checkcov.py @@ -0,0 +1,77 @@ +from __future__ import print_function + +import sys +import glob + + +def main(): + + args = sys.argv + + if ("--help" in args) or (len(sys.argv) < 4): + if len(sys.argv) < 4: + print("ERROR: check_covered requires at least three arguments\n") + print("USAGE: check_covered [--tstl] [--mutantDir directory]") + print(" --mutantDir: directory to put generated mutants in; defaults to current directory") + print(" --tstl: process that is output from TSTL internal report") + sys.exit(0) + + mdir = "." + try: + mdirpos = args.index("--mutantDir") + except ValueError: + mdirpos = -1 + + if mdirpos != -1: + mdir = args[mdirpos + 1] + args.remove("--mutantDir") + args.remove(mdir) + if mdir[-1] != "/": + mdir += "/" + + src = args[1] + coverFile = args[2] + outFile = args[3] + + tstl = "--tstl" in sys.argv + + srcBase = src.split("/")[-1] + srcEnd = src.split(".")[-1] + + with open(coverFile) as file: + if not tstl: + lines = list(map(int, file.read().split())) + else: + lines = [] + for l in file: + if "LINES" in l: + if src not in l: + continue + db = l.split("[")[1] + d = db[:-2].split(",") + for line in d: + lines.append(int(line)) + + with open(outFile, 'w') as coveredFile: + for f in glob.glob( + mdir + + srcBase.replace( + srcEnd, + "mutant.*." + + srcEnd)): + with open(src, 'r') as sf: + sfLines = sf.readlines() + with open(f, 'r') as mf: + mfLines = mf.readlines() + line = 1 + for i in range(0, min(len(sfLines), len(mfLines))): + if sfLines[i] != mfLines[i]: + break + line += 1 + print(f, line) + if line in lines: + coveredFile.write(f.split("/")[-1] + "\n") + + +if __name__ == '__main__': + main() diff --git a/build/lib/universalmutator/comby/c.rules b/build/lib/universalmutator/comby/c.rules new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/universalmutator/comby/c_like.rules b/build/lib/universalmutator/comby/c_like.rules new file mode 100644 index 0000000..0b114c7 --- /dev/null +++ b/build/lib/universalmutator/comby/c_like.rules @@ -0,0 +1,20 @@ +#include ==> DO_NOT_MUTATE + +:[[expr]]:[rest]:[lf~[\n]] ==> /*:[expr]:[rest]*/:[lf] +if (:[cond]) ==> if (!(:[cond])) +if(:[cond]) ==> if ((!:[cond])) +if (:[cond]) ==> if (0==1) +if(:[cond]) ==> if(0==1) +if (:[cond]) ==> if (1==1) +if(:[cond]) ==> if(1==1) +while (:[cond]) ==> while (!(:[cond])) +while(:[cond]) ==> while(!:([cond])) +else ==> + +||:[expr] ==> || (0==1) +:[expr]|| ==> (0==1) || +&&:[expr] ==> && (1==1) +:[expr]&& ==> (1==1) && + +// ==> SKIP_MUTATING_REST + diff --git a/build/lib/universalmutator/comby/cpp.rules b/build/lib/universalmutator/comby/cpp.rules new file mode 100644 index 0000000..b164656 --- /dev/null +++ b/build/lib/universalmutator/comby/cpp.rules @@ -0,0 +1,14 @@ +any_of ==> all_of +any_of ==> none_of +all_of ==> none_of +all_of ==> any_of +none_of ==> any_of +none_of ==> all_of +at(:[hole]) ==> at(0) +at (:[hole]) ==> at (0) +front_inserter ==> back_inserter +back_inserter ==> front_inserter +min_element ==> max_element +max_element ==> min_element +accumulate ==> inner_product +inner_product ==> accumulate diff --git a/build/lib/universalmutator/comby/fortran.rules b/build/lib/universalmutator/comby/fortran.rules new file mode 100644 index 0000000..d5eba99 --- /dev/null +++ b/build/lib/universalmutator/comby/fortran.rules @@ -0,0 +1,112 @@ +\*\* ==> + +\*\* ==> - +\*\* ==> / + +\+ ==> ** +- ==> ** +\* ==> ** +([^\*/])/([^\*/]) ==> \1**\2 + +\.lt\. ==> .eq. +\.lt\. ==> .ne. +\.lt\. ==> .le. +\.lt\. ==> .gt. +\.lt\. ==> .ge. + +\.le\. ==> .eq. +\.le\. ==> .ne. +\.le\. ==> .lt. +\.le\. ==> .gt. +\.le\. ==> .ge. + +\.gt\. ==> .eq. +\.gt\. ==> .ne. +\.gt\. ==> .le. +\.gt\. ==> .lt. +\.gt\. ==> .ge. + +\.ge\. ==> .eq. +\.ge\. ==> .ne. +\.ge\. ==> .le. +\.ge\. ==> .gt. +\.ge\. ==> .lt. + +\.eq\. ==> .ge. +\.ge\. ==> .ne. +\.ge\. ==> .le. +\.ge\. ==> .gt. +\.ge\. ==> .lt. + +\.ne\. ==> .ge. +\.ne\. ==> .eq. +\.ne\. ==> .le. +\.ne\. ==> .gt. +\.ne\. ==> .lt. + +\.and\. ==> .or. +\.and\. ==> .eqv. +\.and\. ==> .neqv. + +\.or\. ==> .and. +\.or\. ==> .eqv. +\.or\. ==> .neqv. + +\.eqv\. ==> .or. +\.eqv\. ==> .and. +\.eqv\. ==> .neqv. + +\.neqv\. ==> .or. +\.neqv\. ==> .and. +\.neqv\. ==> .eqv + +\.LT\. ==> .EQ. +\.LT\. ==> .NE. +\.LT\. ==> .LE. +\.LT\. ==> .GT. +\.LT\. ==> .GE. + +\.LE\. ==> .EQ. +\.LE\. ==> .NE. +\.LE\. ==> .LT. +\.LE\. ==> .GT. +\.LE\. ==> .GE. + +\.GT\. ==> .EQ. +\.GT\. ==> .NE. +\.GT\. ==> .LE. +\.GT\. ==> .LT. +\.GT\. ==> .GE. + +\.GE\. ==> .EQ. +\.GE\. ==> .NE. +\.GE\. ==> .LE. +\.GE\. ==> .GT. +\.GE\. ==> .LT. + +\.EQ\. ==> .GE. +\.GE\. ==> .NE. +\.GE\. ==> .LE. +\.GE\. ==> .GT. +\.GE\. ==> .LT. + +\.NE\. ==> .GE. +\.NE\. ==> .EQ. +\.NE\. ==> .LE. +\.NE\. ==> .GT. +\.NE\. ==> .LT. + +\.AND\. ==> .OR. +\.AND\. ==> .EQV. +\.AND\. ==> .NEQV. + +\.OR\. ==> .AND. +\.OR\. ==> .EQV. +\.OR\. ==> .NEQV. + +\.EQV\. ==> .OR. +\.EQV\. ==> .AND. +\.EQV\. ==> .NEQV. + +\.NEQV\. ==> .OR. +\.NEQV\. ==> .AND. +\.NEQV\. ==> .EQV \ No newline at end of file diff --git a/build/lib/universalmutator/comby/go.rules b/build/lib/universalmutator/comby/go.rules new file mode 100644 index 0000000..eca3edb --- /dev/null +++ b/build/lib/universalmutator/comby/go.rules @@ -0,0 +1,6 @@ +:[[a]] && :[[b]] ==> :[[a]] && true +:[[a]] && :[[b]] ==> true && :[[b]] +:[[a]] || :[[b]] ==> :[[a]] || false +:[[a]] || :[[b]] ==> :[[a]] || false + +defer ==> \ No newline at end of file diff --git a/build/lib/universalmutator/comby/java.rules b/build/lib/universalmutator/comby/java.rules new file mode 100644 index 0000000..d56c50a --- /dev/null +++ b/build/lib/universalmutator/comby/java.rules @@ -0,0 +1,10 @@ +:[s~[ \t]*]import ==> DO_NOT_MUTATE +:[s~[ \t]*]@:[annotation] ==> DO_NOT_MUTATE + + +synchronized ==> + +:[a]&&:[b] ==> :[a] && true +:[a]&&:[b] ==> true && :[b] +:[a]||:[b] ==> :[a] || false +:[a]||:[b] ==> false || :[b] diff --git a/build/lib/universalmutator/comby/lisp.rules b/build/lib/universalmutator/comby/lisp.rules new file mode 100644 index 0000000..62dba25 --- /dev/null +++ b/build/lib/universalmutator/comby/lisp.rules @@ -0,0 +1,8 @@ +and ==> or +or ==> and +not :cond ==> :cond +:cond ==> not :cond +or ==> or T +and ==> and nil +T ==> nil +nil ==> T diff --git a/build/lib/universalmutator/comby/none.rules b/build/lib/universalmutator/comby/none.rules new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/universalmutator/comby/python.rules b/build/lib/universalmutator/comby/python.rules new file mode 100644 index 0000000..807ea2d --- /dev/null +++ b/build/lib/universalmutator/comby/python.rules @@ -0,0 +1,47 @@ +:[s~[ \t]*]import ==> DO_NOT_MUTATE + +if :[cond] ==> if not (:[cond]) +while :[cond] ==> while not (:[cond]) + +:[body]continue ==> :[body] break +:[body]break ==> :[body] continue + +print(:[1], :[2], :[3]) ==> print(:[3], :[2], :[1]) +:[[v]] = :[[fn]](:[x]) ==> :[[v]] = :[[fn]]((:[x] - 1)) + +:[[a]] and :[[b]] ==> :[[a]] or :[[b]] +:[[a]] or :[[b]] ==> :[[a]] and :[[b]] +:[[a]] and :[[b]] ==> :[[a]] or True +:[[a]] or :[[b]] ==> :[[a]] or False +:[[a]] and :[[b]] ==> True and :[[b]] +:[[a]] or :[[b]] ==> False or :[[b]] + +not :[[a]] ==> :[[a]] + +return :[expr]:[lf~[\n]] ==> return None:[lf] +:[ s]:[expr]:[lf~[\n]] ==> :[s]pass:[lf] + +//:[[a]] ==> /:[[a]] +/:[[a]] ==> //:[[a]] + +True ==> False + +[:[expr]] ==> [] +[:[first],:[rest]] ==> [:[first]] +[:[first],:[rest]] ==> [:[rest]] +,:[s~[ \t]*]:[[x]]:[bra~[\]]] ==> ] + +{:[expr]} ==> {} +{:[first],:[rest]} ==> {:[first]} +{:[first],:[rest]} ==> {:[rest]} +,:[s~[ \t]*]:[[x]]:[bra~[\}]] ==> } + + +,:[s~[ \t]*]:[[item1]],:[s~[ \t]*]:[[item2]] ==> ,:[[item2]],:[[item1]] +:[par~[(]]:[~[ \t]*]:[[item1]],:[~[ \t]*]:[[item2]] ==> :[par]:[[item2]],:[[item1]] +:[bra~[\[]]:[~[ \t]*]:[[item1]],:[~[ \t]*]:[[item2]] ==> :[bra]:[[item2]],:[[item1]] + +,:[item], ==> , +':[str]' ==> '' + +@:[s~[ \t]*]:[annotation] :[body] ==> :[body] \ No newline at end of file diff --git a/build/lib/universalmutator/comby/r.rules b/build/lib/universalmutator/comby/r.rules new file mode 100644 index 0000000..1c69a9e --- /dev/null +++ b/build/lib/universalmutator/comby/r.rules @@ -0,0 +1 @@ +# ==> SKIP_MUTATING_REST \ No newline at end of file diff --git a/build/lib/universalmutator/comby/rust.rules b/build/lib/universalmutator/comby/rust.rules new file mode 100644 index 0000000..26b8bd2 --- /dev/null +++ b/build/lib/universalmutator/comby/rust.rules @@ -0,0 +1,2 @@ +true ==> false +false ==> true diff --git a/build/lib/universalmutator/comby/solidity.rules b/build/lib/universalmutator/comby/solidity.rules new file mode 100644 index 0000000..05e69c7 --- /dev/null +++ b/build/lib/universalmutator/comby/solidity.rules @@ -0,0 +1,44 @@ +:[prefix~[^u]]fixed ==> :[prefix]ufixed +ufixed ==> fixed +int16 ==> int8 +int32 ==> int16 +int64 ==> int32 +memory ==> storage +storage ==> memory +view ==> pure +pure ==> view +constant ==> pure +payable ==> +private ==> +msg.sender ==> tx.origin +tx.origin ==> msg.sender +ether ==> wei +:[prefix~wei|finney|szabo] ==> ether +:[prefix~ether|finney|szabo] ==> wei +:[prefix~wei|ether|szao] ==> finney +:[prefix~wei|finney|ether] ==> szabo +:[prefix~minutes|days|hours|weeks|years] ==> seconds +:[prefix~seconds|days|hours|weeks|years] ==> minutes +:[prefix~seconds|minutes|hours|weeks|years] ==> days +:[prefix~seconds|minutes|days|weeks|years] ==> hours +:[prefix~seconds|minutes|days|hours|years] ==> weeks +:[prefix~seconds|minutes|days|hours|weeks] ==> years +now ==> 0 +block.timestamp ==> 0 +msg.value ==> 0 +msg.value ==> 1 +addmod ==> mulmod +mulmod ==> addmod +:[[expr]] ==> selfdestruct(msg.sender); +:[[expr]] ==> revert(); +call ==> delegatecall +delegatecall ==> call +call ==> callcode +callcode ==> call +delegatecall ==> callcode +callcode ==> delegatecall +:[[v1]] :[[v2]] ==> :[[v1]]:[[v2]] +if (:[expr]) ==> if (false) +if(:[expr]) ==> if(false) +if (:[expr]) ==> if (true) +if(:[expr]) ==> if(true) diff --git a/build/lib/universalmutator/comby/swift.rules b/build/lib/universalmutator/comby/swift.rules new file mode 100644 index 0000000..b6d8cdc --- /dev/null +++ b/build/lib/universalmutator/comby/swift.rules @@ -0,0 +1,13 @@ +var :[[a]] ==> let :[[a]] + +true ==> false +false ==> true + +[:[args]] ==> [] + +,:[[v1]], :[[v2]] ==> , + +:[[a]] && :[[b]] ==> :[[a]] && true +:[[a]] && :[[b]] ==> true && :[[b]] +:[[a]] || :[[b]] ==> :[[a]] || false +:[[a]] || :[[b]] ==> :[[a]] || false \ No newline at end of file diff --git a/build/lib/universalmutator/comby/universal.rules b/build/lib/universalmutator/comby/universal.rules new file mode 100644 index 0000000..ab17a3b --- /dev/null +++ b/build/lib/universalmutator/comby/universal.rules @@ -0,0 +1,96 @@ +DO_NOT_MUTATE ==> DO_NOT_MUTATE + +:[a]+:[b] ==> :[a]-:[b] +:[a]+:[b] ==> :[a]*:[b] +:[a]+:[b] ==> :[a]/:[b] +:[a]+:[b] ==> :[a]%:[b] + +:[a]-:[b] ==> :[a]+:[b] +:[a]-:[b] ==> :[a]*:[b] +:[a]-:[b] ==> :[a]/:[b] +:[a]-:[b] ==> :[a]%:[b] + +:[a]*:[b] ==> :[a]+:[b] +:[a]*:[b] ==> :[a]-:[b] +:[a]*:[b] ==> :[a]/:[b] +:[a]*:[b] ==> :[a]%:[b] + +:[a]/:[b] ==> :[a]+:[b] +:[a]/:[b] ==> :[a]-:[b] +:[a]/:[b] ==> :[a]*:[b] +:[a]/:[b] ==> :[a]%:[b] + +:[a]%:[b] ==> :[a]+:[b] +:[a]%:[b] ==> :[a]-:[b] +:[a]%:[b] ==> :[a]*:[b] +:[a]%:[b] ==> :[a]/:[b] + +:[a]!=:[b] ==> :[a]==:[b] +:[a]!=:[b] ==> :[a]<=:[b] +:[a]!=:[b] ==> :[a]>=:[b] +:[a]!=:[b] ==> :[a]>:[b] +:[a]!=:[b] ==> :[a]<:[b] + +:[a]==:[b] ==> :[a]!=:[b] +:[a]==:[b] ==> :[a]<=:[b] +:[a]==:[b] ==> :[a]>=:[b] +:[a]==:[b] ==> :[a]>:[b] +:[a]==:[b] ==> :[a]<:[b] + +:[a]>=:[b] ==> :[a]==:[b] +:[a]>=:[b] ==> :[a]!=:[b] +:[a]>=:[b] ==> :[a]<:[b] +:[a]>=:[b] ==> :[a]>:[b] + +:[a]<=:[b] ==> :[a]==:[b] +:[a]<=:[b] ==> :[a]!=:[b] +:[a]<=:[b] ==> :[a]<:[b] +:[a]<=:[b] ==> :[a]>:[b] + +:[a]<:[b] ==> :[a]>:[b] +:[a]<:[b] ==> :[a]==:[b] +:[a]<:[b] ==> :[a]<=:[b] +:[a]<:[b] ==> :[a]>=:[b] +:[a]<:[b] ==> :[a]!=:[b] + +:[a]>:[b] ==> :[a]<:[b] +:[a]>:[b] ==> :[a]==:[b] +:[a]>:[b] ==> :[a]>=:[b] +:[a]>:[b] ==> :[a]<=:[b] +:[a]>:[b] ==> :[a]!=:[b] + +:[a]+=:[b] ==> :[a]=+:[b] +:[a]-=:[b] ==> :[a]=-:[b] + +:[a]-- ==> :[a]++ +:[a]++ ==> :[a]-- + +:[neg~-]:[a] ==> :[a] + +:[a~\D]:[number~\d+]:[b~\D] ==> :[a]0:[b] +:[a~\D]:[number~\d+]:[b~\D] ==> :[a]1:[b] +:[a~\D]:[number~\d+]:[b~\D] ==> :[a]-1:[b] +:[a~\D]:[number~\d+]:[b~\D] ==> :[a](:[number]+1):[b] +:[a~\D]:[number~\d+]:[b~\D] ==> :[a](:[number]-1):[b] + +:[a]&&:[b] ==> :[a]||:[b] +:[a]||:[b] ==> :[a]&&:[b] +!:[a] ==> :[a] + +:[a]&:[b] ==> :[a]|:[b] +:[a]|:[b] ==> :[a]&:[b] + +:[s~[ \t]*]:[[a]]:[b]:[lf~[\n]] ==> :[s]:[a]:[b]:[lf]:[s]break;:[lf] +:[s~[ \t]*]:[[a]]:[b]:[lf~[\n]] ==> :[s]:[a]:[b]:[lf]:[s]continue;:[lf] + +":[str]" ==> "" + +while ==> if + +:[lead~[,\(\[\{]]:[v1~[^,=\)\}\]]+],:[v2~[^,=\)\}\]]+]:[end~[,\)\]\}]] ==> :[lead]:[v2],:[v1]:[end] +:[lead~[,\(\[\{]]:[name1~[^,=\)\}\]]+]=:[val1~[^,=\)\}\]]+],:[name2~[^,=\)\}\]]+]=:[val2~[^,=\)\}\]]+]:[end~[,\)\]\}]] ==> :[lead]:[name2]=:[val1],:[name1]=:[val2]:[end] + +min ==> max +max ==> min +begin ==> end +end ==> begin \ No newline at end of file diff --git a/build/lib/universalmutator/comby/vyper.rules b/build/lib/universalmutator/comby/vyper.rules new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/universalmutator/cpp_handler.py b/build/lib/universalmutator/cpp_handler.py new file mode 100644 index 0000000..2222de5 --- /dev/null +++ b/build/lib/universalmutator/cpp_handler.py @@ -0,0 +1,4 @@ +dumb = True + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + return "VALID" diff --git a/build/lib/universalmutator/fe_handler.py b/build/lib/universalmutator/fe_handler.py new file mode 100644 index 0000000..b26d56c --- /dev/null +++ b/build/lib/universalmutator/fe_handler.py @@ -0,0 +1,45 @@ +import glob +import os +import subprocess +import shutil + + +def extractOpcodes(text, filename): + return text + + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + outName = ".um.out." + str(os.getpid()) + ".opcodes" + if len(uniqueMutants) == 0: + shutil.copy(tmpMutantName, tmpMutantName + ".backup." + str(os.getpid())) + shutil.copy(sourceFile, tmpMutantName) + try: + shutil.rmtree(".tmp_mutant_fe") + except EnvironmentError: + pass + with open(outName, 'w') as file: + r = subprocess.call( + ["fe", tmpMutantName, "--emit", "yul", "--overwrite", "-o", ".tmp_mutant_fe"], stdout=file, stderr=file) + code = "" + for yulf in glob.glob(".tmp_mutant_fe/*/*.yul"): + with open(yulf, 'r') as file: + code += extractOpcodes(file.read(), tmpMutantName) + uniqueMutants[code] = 1 + try: + shutil.rmtree(".tmp_mutant_fe") + except EnvironmentError: + pass + with open(outName, 'w') as file: + r = subprocess.call(["fe", tmpMutantName, "--emit", + "yul", "--overwrite", "-o", ".tmp_mutant_fe"], stdout=file, stderr=file) + if r == 0: + code = "" + for yulf in glob.glob(".tmp_mutant_fe/*/*.yul"): + with open(yulf, 'r') as file: + code += extractOpcodes(file.read(), tmpMutantName) + if code in uniqueMutants: + uniqueMutants[code] += 1 + return "REDUNDANT" + uniqueMutants[code] = 1 + return "VALID" + return "INVALID" diff --git a/build/lib/universalmutator/findmissing.py b/build/lib/universalmutator/findmissing.py new file mode 100644 index 0000000..457b904 --- /dev/null +++ b/build/lib/universalmutator/findmissing.py @@ -0,0 +1,54 @@ +from __future__ import print_function +import glob +import sys + +from universalmutator import utils + + +def main(): + + f = sys.argv[1] + d1 = sys.argv[2] + d2 = sys.argv[3] + + fsplit = f.split(".") + pattern = "/" + fsplit[0] + ".mutant.*." + fsplit[-1] + d1files = glob.glob(d1 + pattern) + d2files = glob.glob(d2 + pattern) + d1contents = [] + d2contents = [] + d1all = [] + d2all = [] + for d1f in d1files: + with open(d1f, "r") as d1c: + r = d1c.read() + if r not in d1all: + d1all.append(r) + else: + print(d1f, "IS REDUNDANT!") + d1contents.append((d1f, r)) + for d2f in d2files: + with open(d2f, "r") as d2c: + r = d2c.read() + if r not in d2all: + d2all.append(r) + else: + print(d2f, "IS REDUNDANT!") + d2contents.append((d2f, r)) + + just1c = list(map(lambda x: x[1], d1contents)) + just2c = list(map(lambda x: x[1], d2contents)) + print("="*80) + for (mf, c) in d1contents: + if c not in just2c: + m = utils.readMutant(mf, f) + utils.show(m) + print("="*80) + for (mf, c) in d2contents: + if c not in just1c: + m = utils.readMutant(mf, f) + utils.show(m) + + +if __name__ == '__main__': + main() diff --git a/build/lib/universalmutator/fortran_handler.py b/build/lib/universalmutator/fortran_handler.py new file mode 100644 index 0000000..2222de5 --- /dev/null +++ b/build/lib/universalmutator/fortran_handler.py @@ -0,0 +1,4 @@ +dumb = True + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + return "VALID" diff --git a/build/lib/universalmutator/genmutants.py b/build/lib/universalmutator/genmutants.py new file mode 100644 index 0000000..e7352a0 --- /dev/null +++ b/build/lib/universalmutator/genmutants.py @@ -0,0 +1,623 @@ +from __future__ import print_function +from tabulate import tabulate + +import os +import random +from re import T +import sys +import shutil +import subprocess + +from universalmutator import mutator + +from universalmutator import c_handler +from universalmutator import cpp_handler +from universalmutator import python_handler +from universalmutator import java_handler +from universalmutator import javascript_handler +from universalmutator import swift_handler +from universalmutator import rust_handler +from universalmutator import go_handler +from universalmutator import lisp_handler +from universalmutator import solidity_handler +from universalmutator import vyper_handler +from universalmutator import fe_handler +from universalmutator import r_handler +from universalmutator import fortran_handler + +def nullHandler(tmpMutantName, mutant, sourceFile, uniqueMutants): + return "VALID" + +def fastCheckLine(mutant, source, sourceFile, uniqueMutants, compileFile, handler, deadCodeLines, interestingLines, tmpMutantName, lineNo): + if compileFile is None: + mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants) + else: + mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants, compileFile=compileFile) + if mutantResult in ["VALID", "REDUNDANT"]: + deadCodeLines.append(lineNo) + print("LINE", str(lineNo) + ":", source[lineNo - 1][:-1], end=" ") + print("APPEARS TO BE COMMENT OR DEAD CODE, SKIPPING...") + else: + interestingLines.append(lineNo) + +def checkCombyDeadCode(deadCodeLines, mutant): + for i in range(mutant[3][0], mutant[3][1]+1): + if i not in deadCodeLines: + return False + return True + +def cmdHandler(tmpMutantName, mutant, sourceFile, uniqueMutants): + if "MUTANT" not in cmd: + # We asssume if the MUTANT isn't part of the command, + # we need to move it into place, before, e.g., make + backupName = sourceFile + ".um.backup." + str(os.getpid()) + shutil.copy(sourceFile, backupName) + shutil.copy(tmpMutantName, sourceFile) + try: + with open(".um.mutant_output." + str(os.getpid()), 'w') as file: + r = subprocess.call([cmd.replace("MUTANT", tmpMutantName)], + shell=True, stderr=file, stdout=file) + if r == 0: + return "VALID" + return "INVALID" + finally: + # If we moved the mutant in, restore original + if "MUTANT" not in cmd: + shutil.copy(backupName, sourceFile) + + +def toGarbage(code): + newCode = "" + for c in code: + if c == "(": + newCode += " " + elif c.isspace() or c in ["*", "/", "#", "-"]: + newCode += c + else: + newCode += "Q" + return newCode + +cmd = None + +def main(): + global cmd + + try: + import custom_handler + except BaseException: + pass + + args = sys.argv + + languages = {".c": "c", + ".h": "c", + ".cpp": "cpp", + ".c++": "cpp", + ".cc": "cpp", + ".hpp": "cpp", + ".py": "python", + ".java": "java", + ".js": "javascript", + ".ts": "javascript", + ".swift": "swift", + ".rs": "rust", + ".go": "go", + ".lisp": "lisp", + ".lsp": "lisp", + ".f": "fortran", + ".f90": "fortran", + ".for": "fortran", + ".R": "r", + ".sol": "solidity", + ".vy": "vyper", + ".fe": "fe"} + + print("*** UNIVERSALMUTATOR ***") + + if ("--help" in args) or (len(sys.argv) < 2): + if len(sys.argv) < 2: + print("ERROR: mutate requires at least one argument (a file to mutate)\n") + print("USAGE: mutate [] [ ...]", + "[--noCheck] [--cmd ] [--mutantDir ]", + "[--lines [--tstl]] [--mutateTestCode] [--mutateBoth]", + "[--ignore ] [--compile ] [--noFastCheck] [--swap]", + "[--redundantOK] [--showRules] [--only ]") + print() + print(" --noCheck: skips compilation/comparison and just generates mutant files") + print(" --cmd executes command string, replacing MUTANT with the mutant name, and uses return code") + print(" to determine mutant validity") + print(" --mutantDir: directory to put generated mutants in; defaults to current directory") + print(" --lines: only generate mutants for lines contained in ") + print(" --tstl: is TSTL output") + print(" --mutateInStrings: mutate inside strings (not just turn to empty string)") + print(" --mutateTestCode: mutate only test code") + print(" --mutateBoth: mutate both test and normal code") + print(" --ignore : ignore lines matching patterns in ") + print(" --compile : compile instead of source (solidity handler only)") + print(" --comby: use comby as the method of mutating code") + print(" --noFastCheck: do not use fast dead code/comment detection heuristic") + print(" --swap: also try adjacent-code swaps") + print(" --redundantOK: keep redundant mutants (for compiler output issues)") + print(" --showRules: show rule source used to generate each mutant") + print(" --only : only use rule file ") + print(" --printStat: print stats for the rules and generated mutants into files") + print() + print("Currently supported languages: ", ", ".join(list(set(languages.values())))) + print("If not supplying a command to compile/build, you should use --noCheck for C, C++,") + print("javascript, and other languages with only a default handler.") + sys.exit(0) + + noCheck = False + if "--noCheck" in args: + noCheck = True + args.remove("--noCheck") + + comby = False + if "--comby" in args: + comby = True + args.remove("--comby") + + redundantOK = False + if "--redundantOK" in args: + redundantOK = True + args.remove("--redundantOK") + + showRules = False + if "--showRules" in args: + showRules = True + args.remove("--showRules") + + mutateInStrings = False + if "--mutateInStrings" in args: + mutateInStrings = True + args.remove("--mutateInStrings") + + mutateTestCode = False + if "--mutateTestCode" in args: + mutateTestCode = True + args.remove("--mutateTestCode") + + mutateBoth = False + if "--mutateBoth" in args: + mutateBoth = True + args.remove("--mutateBoth") + + noFastCheck = False + if "--noFastCheck" in args: + noFastCheck = True + args.remove("--noFastCheck") + + doSwaps = False + if "--swap" in args: + doSwaps = True + args.remove("--swap") + + tstl = False + if "--tstl" in args: + tstl = True + args.remove("--tstl") + + fuzz = False + if "--fuzz" in args: + fuzz = True + args.remove("--fuzz") + + printStat = False + if "--printStat" in args: + printStat = True + args.remove("--printStat") + + cmd = None + try: + cmdpos = args.index("--cmd") + except ValueError: + cmdpos = -1 + + if cmdpos != -1: + cmd = args[cmdpos + 1] + args.remove("--cmd") + args.remove(cmd) + + sourceFile = args[1] + ending = "." + sourceFile.split(".")[-1] + + lineFile = None + try: + linepos = args.index("--lines") + except ValueError: + linepos = -1 + + if linepos != -1: + lineFile = args[linepos + 1] + args.remove("--lines") + args.remove(lineFile) + + if lineFile is not None: + with open(lineFile) as file: + if not tstl: + lines = list(map(int, file.read().split())) + else: + lines = [] + for l in file: + if "LINES" in l: + if sourceFile not in l: + continue + db = l.split("[")[1] + d = db[:-2].split(",") + for line in d: + lines.append(int(line)) + + mdir = "." + try: + mdirpos = args.index("--mutantDir") + except ValueError: + mdirpos = -1 + + if mdirpos != -1: + mdir = args[mdirpos + 1] + args.remove("--mutantDir") + args.remove(mdir) + if mdir[-1] != "/": + mdir += "/" + + ignoreFile = None + try: + ignorepos = args.index("--ignore") + except ValueError: + ignorepos = -1 + + if ignorepos != -1: + ignoreFile = args[ignorepos + 1] + args.remove("--ignore") + args.remove(ignoreFile) + + compileFile = None + try: + compilepos = args.index("--compile") + except ValueError: + compilepos = -1 + + if compilepos != -1: + compileFile = args[compilepos + 1] + args.remove("--compile") + args.remove(compileFile) + + ignorePatterns = [] + if ignoreFile is not None: + print("IGNORING PATTERNS DEFINED IN", ignoreFile) + with open(ignoreFile, 'r') as ignoref: + for l in ignoref: + ignorePatterns.append(l[:-1]) + + handlers = {"python": python_handler, + "c": c_handler, + "c++": cpp_handler, + "cpp": cpp_handler, + "java": java_handler, + "javascript": javascript_handler, + "swift": swift_handler, + "rust": rust_handler, + "r": r_handler, + "fortran": fortran_handler, + "go": go_handler, + "lisp": lisp_handler, + "solidity": solidity_handler, + "vyper": vyper_handler, + "fe": fe_handler} + + cLikeLanguages = [ + "c", + "java", + "javascript", + "swift", + "cpp", + "c++", + "rust", + "solidity", + "go"] + + try: + handlers["custom"] == custom_handler + except BaseException: + pass + + sourceFile = args[1] + base = (".".join((sourceFile.split(".")[:-1]))).split("/")[-1] + ending = "." + sourceFile.split(".")[-1] + + if "--only" not in args: + if len(args) < 3: + try: + language = languages[ending] + except KeyError: + language = "none" + otherRules = [] + else: + if ".rules" in args[2]: + language = languages[ending] + otherRules = args[2:] + else: + language = args[2] + otherRules = args[3:] + + if language not in handlers: + if language.lower() in handlers: + language = language.lower() + + if language in cLikeLanguages: + otherRules.append("c_like.rules") + + if language == "vyper": + otherRules.append("python.rules") + otherRules.append("solidity.rules") + + if language == "fe": + otherRules.append("python.rules") + otherRules.append("solidity.rules") + + rules = ["universal.rules", language + ".rules"] + otherRules + if fuzz: + if language == "none": + fuzzRules = ["universal.rules", "c_like.rules", "python.rules", "vyper.rules", "solidity.rules"] + rules = list(set(fuzzRules + rules)) + else: + onlyPos = args.index("--only") + rules = [args[onlyPos + 1]] + if args[2] != "--only": + language = args[2] + else: + try: + language = languages[ending] + except KeyError: + language = "none" + if language not in handlers: + if language.lower() in handlers: + language = language.lower() + + source = [] + + with open(sourceFile, 'r') as file: + for line in file: + # remove non-ascii characters (comby issue) + line_processed = line.encode('ascii', 'ignore').decode() + source.append(line_processed) + + mutants = [] + + if comby: + mutants = mutator.mutants_comby(source, ruleFiles=rules, mutateTestCode=mutateTestCode, mutateBoth=mutateBoth, + ignorePatterns=ignorePatterns, ignoreStringOnly=not mutateInStrings, fuzzing=fuzz, language=ending) + else: + mutants = mutator.mutants_regexp(source, ruleFiles=rules, mutateTestCode=mutateTestCode, mutateBoth=mutateBoth, + ignorePatterns=ignorePatterns, ignoreStringOnly=not mutateInStrings, fuzzing=fuzz) + if fuzz: + if len(mutants) == 0: + sys.exit(255) + mutants = [random.choice(mutants)] # Just pick one + + print(len(mutants), "MUTANTS GENERATED BY RULES") + + validMutants = [] + invalidMutants = [] + redundantMutants = [] + uniqueMutants = {} + sourceJoined = '' + + if comby: + noFastCheck = True + + dumbHandler = False + if not noCheck: + if cmd is not None: + handler = cmdHandler + elif language == "none": + handler = nullHandler + else: + try: + if handlers[language].dumb: + noFastCheck = True + dumbHandler = True + except BaseException: + pass + handler = handlers[language].handler + else: + handler = nullHandler + noFastCheck = True + + deadCodeLines = [] + interestingLines = [] + + tmpMutantName = ".tmp_mutant." + str(os.getpid()) + ending + mutantNo = 0 + for mutant in mutants: + if (lineFile is not None) and mutant[0] not in lines: + # skip if not a line to mutate + continue + if not noFastCheck: + if comby: + checkLines = [] + for i in range(mutant[3][0], mutant[3][1] + 1): + if i not in deadCodeLines or i not in interestingLines: + checkLines.append(i) + for lineNo in checkLines: + fastCheckMutant = (lineNo, toGarbage(source[lineNo - 1])) + mCreated = mutator.makeMutant(source, fastCheckMutant, tmpMutantName) + if mCreated: + fastCheckLine(mutant, source, sourceFile, uniqueMutants, compileFile, handler, deadCodeLines, interestingLines, tmpMutantName, lineNo) + if checkCombyDeadCode(deadCodeLines, mutant): + continue + else: + if (mutant[0] not in interestingLines) and (mutant[0] not in deadCodeLines): + fastCheckMutant = (mutant[0], toGarbage(source[mutant[0] - 1])) + mCreated = mutator.makeMutant(source, fastCheckMutant, tmpMutantName) + if mCreated: + fastCheckLine(mutant, source, sourceFile, uniqueMutants, compileFile, handler, deadCodeLines, interestingLines, tmpMutantName, mutant[0]) + if mutant[0] in deadCodeLines: + continue + + if comby: + sourceJoined = ''.join(source) + print("PROCESSING MUTANT:", + "range" + str(mutant[0]) + ":", sourceJoined[mutant[0][0]:mutant[0][1]].replace("\n", "\\n"), " ==> ", mutant[1], end="...") + else: + print("PROCESSING MUTANT:", + str(mutant[0]) + ":", source[mutant[0] - 1][:-1], " ==> ", mutant[1][:-1], end="...") + if (not comby) and showRules: + print("(FROM:", mutant[2][1], end=")...") + + if comby: + mCreated = mutator.makeMutantComby(sourceJoined, mutant, tmpMutantName) + else: + mCreated = mutator.makeMutant(source, mutant, tmpMutantName) + if not mCreated: + print("REDUNDANT (SOURCE COPY!)") + redundantMutants.append(mutant) + continue + + if compileFile is None: + mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants) + else: + mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants, compileFile=compileFile) + print(mutantResult, end=" ") + mutantName = mdir + base + ".mutant." + str(mutantNo) + ending + if fuzz: + mutantName = mdir + "fuzz.out" + if (mutantResult == "VALID") or (mutantResult == "REDUNDANT" and redundantOK): + print("[written to", mutantName + "]", end=" ") + shutil.copy(tmpMutantName, mutantName) + validMutants.append(mutant) + mutantNo += 1 + elif mutantResult == "INVALID": + invalidMutants.append(mutant) + elif mutantResult == "REDUNDANT": + redundantMutants.append(mutant) + print() + sys.stdout.flush() + + if doSwaps: + print("TRYING CODE SWAPS...") + swapList = [] + for lineNo in range(len(source)): + if lineNo + 1 in deadCodeLines: + continue + swapList.append(lineNo) + for i in range(0, len(swapList)-1): + a = swapList[i] + b = swapList[i+1] + mutant = [a + 1] # Only the line is valid here + print("TRYING TO SWAP LINES", a + 1, "AND", b + 1, end="...") + newSource = source[:a] + newSource.append(source[b]) + newSource += source[a+1:b] + newSource.append(source[a]) + newSource += source[b+1:] + with open(tmpMutantName, 'w') as file: + for line in newSource: + file.write(line) + if compileFile is None: + mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants) + else: + mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants, compileFile=compileFile) + print(mutantResult, end=" ") + mutantName = mdir + base + ".mutant." + str(mutantNo) + ending + if (mutantResult == "VALID") or (mutantResult == "REDUNDANT" and redundantOK): + print("[written to", mutantName + "]", end=" ") + shutil.copy(tmpMutantName, mutantName) + validMutants.append(mutant) + mutantNo += 1 + elif mutantResult == "INVALID": + invalidMutants.append(mutant) + elif mutantResult == "REDUNDANT": + redundantMutants.append(mutant) + print() + sys.stdout.flush() + + print(len(validMutants), "VALID MUTANTS") + print(len(invalidMutants), "INVALID MUTANTS") + print(len(redundantMutants), "REDUNDANT MUTANTS") + + totalMutants = len(validMutants) + len(invalidMutants) + len(redundantMutants) + valid_rate = 0 if totalMutants == 0 else (len(validMutants) * 100.0)/totalMutants + print(f"Valid Percentage: {valid_rate}%") + + (rules, _, _) = mutator.parseRules(rules, comby= comby) + + if printStat: + source = sourceJoined if comby else None + printMutantsStat((validMutants, invalidMutants, redundantMutants), source) + printRulesStat(rules, validMutants, invalidMutants) + + if dumbHandler: + print() + print("*"*80) + print("WARNING: because the handler does not compile and so has no pruning support,") + print("all mutants were considered valid. Consider using --cmd to build the target!") + print("*"*80) + print() + + try: + os.remove(tmpMutantName) + except BaseException: + pass + +def printMutantsStat(mutants, source = None): + def dumpToFile(fileName, mutants): + fis = open(fileName, "w") + i = 0 + for mutant in mutants: + i += 1 + sys.stdout.flush() + + fis.write(f"{i}.\n") + fis.write(mutant[2][0]) + fis.write('\n') + if source is not None: + fis.write("source:\n") + fis.write(source[mutant[0][0]:mutant[0][1]]) + fis.write('\n') + fis.write("mutant:\n") + fis.write(mutant[1]) + fis.write('\n\n') + fis.close() + + validMutants, invalidMutants, redundantMutants = mutants + dumpToFile('valid_mutants.txt', validMutants) + dumpToFile('invalid_mutants.txt', invalidMutants) + dumpToFile('redundant_mutants.txt', redundantMutants) + +def printRulesStat(rules, validMutants, invalidMutants): + valid_cnt = {} + invalid_cnt = {} + + for mutant in validMutants: + lhs, rhs = mutant[-1] + if (lhs,rhs) not in valid_cnt: + valid_cnt[(lhs,rhs)] = 0 + valid_cnt[(lhs,rhs)] += 1 + + for mutant in invalidMutants: + lhs, rhs = mutant[-1] + if (lhs,rhs) not in invalid_cnt: + invalid_cnt[(lhs,rhs)] = 0 + invalid_cnt[(lhs,rhs)] += 1 + + fis = open("rules_count.txt", "w") + i = 0 + table = [] + table.append(["#","Rule","No. of Valids", "No. of Invalids"]) + + for ((lhs, rhs), _) in rules: + if (lhs,rhs) not in valid_cnt: + valid_cnt[(lhs,rhs)] = 0 + if (lhs,rhs) not in invalid_cnt: + invalid_cnt[(lhs,rhs)] = 0 + i += 1 + + table.append([f'{i}',f'{lhs} ==> {rhs}', f'{valid_cnt[(lhs,rhs)]}', f'{invalid_cnt[(lhs,rhs)]}']) + + fis.write(tabulate(table,tablefmt="grid")) + sys.stdout.flush() + fis.close() + +if __name__ == '__main__': + main() diff --git a/build/lib/universalmutator/go_handler.py b/build/lib/universalmutator/go_handler.py new file mode 100644 index 0000000..2222de5 --- /dev/null +++ b/build/lib/universalmutator/go_handler.py @@ -0,0 +1,4 @@ +dumb = True + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + return "VALID" diff --git a/build/lib/universalmutator/intersect.py b/build/lib/universalmutator/intersect.py new file mode 100644 index 0000000..e960dd2 --- /dev/null +++ b/build/lib/universalmutator/intersect.py @@ -0,0 +1,33 @@ +from __future__ import print_function +import sys + + +def main(): + + args = sys.argv + + if ("--help" in args) or (len(sys.argv) < 2): + if len(sys.argv) < 2: + print("ERROR: intersect_mutants requires at least three arguments\n") + print("USAGE: intersect_mutants ") + sys.exit(0) + + infile1 = sys.argv[1] + infile2 = sys.argv[2] + outfile = sys.argv[3] + + infile1_mutants = [] + with open(infile1, 'r') as if1: + for line in if1: + infile1_mutants.append(line.split()[0]) + + with open(outfile, 'w') as outf: + with open(infile2, 'r') as if2: + for line in if2: + m = line.split()[0] + if m in infile1_mutants: + outf.write(line) + + +if __name__ == '__main__': + main() diff --git a/build/lib/universalmutator/java_handler.py b/build/lib/universalmutator/java_handler.py new file mode 100644 index 0000000..6257778 --- /dev/null +++ b/build/lib/universalmutator/java_handler.py @@ -0,0 +1,26 @@ +import os +import subprocess +import shutil + + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + backupName = sourceFile + ".um.backup." + str(os.getpid()) + classFile = sourceFile.replace(".java", ".class") + classBackupName = classFile + ".um.backup" + str(os.getpid()) + try: + shutil.copy(sourceFile, backupName) + if os.path.exists(classFile): + shutil.copy(classFile, classBackupName) + shutil.copy(tmpMutantName, sourceFile) + with open(".um.mutant_output" + str(os.getpid()), 'w') as file: + r = subprocess.call(["javac", sourceFile], + stdout=file, stderr=file) + finally: + shutil.copy(backupName, sourceFile) + os.remove(backupName) + if os.path.exists(classBackupName): + shutil.copy(classBackupName, classFile) + os.remove(classBackupName) + if r == 0: + return "VALID" + return "INVALID" diff --git a/build/lib/universalmutator/javascript_handler.py b/build/lib/universalmutator/javascript_handler.py new file mode 100644 index 0000000..2222de5 --- /dev/null +++ b/build/lib/universalmutator/javascript_handler.py @@ -0,0 +1,4 @@ +dumb = True + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + return "VALID" diff --git a/build/lib/universalmutator/lisp_handler.py b/build/lib/universalmutator/lisp_handler.py new file mode 100644 index 0000000..2222de5 --- /dev/null +++ b/build/lib/universalmutator/lisp_handler.py @@ -0,0 +1,4 @@ +dumb = True + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + return "VALID" diff --git a/build/lib/universalmutator/mutator.py b/build/lib/universalmutator/mutator.py new file mode 100644 index 0000000..def4003 --- /dev/null +++ b/build/lib/universalmutator/mutator.py @@ -0,0 +1,298 @@ +from __future__ import print_function +import re +import pkg_resources +import random +from comby import Comby +import os +from json.decoder import JSONDecodeError + +def parseRules(ruleFiles, comby=False): + rulesText = [] + + for ruleFile in ruleFiles: + if ".rules" not in ruleFile: + ruleFile += ".rules" + try: + if comby: + rulePath = os.path.join('comby', ruleFile) + else: + rulePath = os.path.join('static', ruleFile) + with pkg_resources.resource_stream('universalmutator', rulePath) as builtInRule: + for line in builtInRule: + line = line.decode() + rulesText.append((line, "builtin:" + ruleFile)) + except BaseException: + print("FAILED TO FIND RULE", ruleFile, "AS BUILT-IN...") + try: + with open(ruleFile, 'r') as file: + for l in file: + rulesText.append((l, ruleFile)) + except BaseException: + print("COULD NOT FIND RULE FILE", ruleFile + "! SKIPPING...") + + rules = [] + ignoreRules = [] + skipRules = [] + ruleLineNo = 0 + + for (r, ruleSource) in rulesText: + ruleLineNo += 1 + + # remove all leading and trailing white space + line = r.strip() + + # check for blank lines + if line == "": + # ignore blank lines + continue + + # handle comments + if line.startswith("#") and "==>" not in line: + # ignore comments '#' + continue + + # check for disabled rules + if line.startswith("# DISABLED:"): + # ignore disabled rules + continue + + # check and parse valid rules + if " ==> " in line: + s = line.split(" ==> ") + elif " ==>" in line: + s = line.split(" ==>") + else: + # otherwise it's a invalid line and warn user + print("*" * 60) + print("WARNING:") + print("RULE:", line, "FROM", ruleSource) + print("DOES NOT MATCH EXPECTED FORMAT, AND SO WAS IGNORED") + print("*" * 60) + continue + + # End of possible fix + + if comby: + lhs = s[0] + lhs = lhs.rstrip() # Trailing whitespace in rule file will be treated significantly unless stripped, so strip it. If matching trailing whitespace is desired, then regex holes should be used. + else: + try: + lhs = re.compile(s[0]) + except BaseException: + print("*" * 60) + print("FAILED TO COMPILE RULE:", r, "FROM", ruleSource) + print("*" * 60) + continue + if (len(s[1]) > 0) and (s[1][-1] == "\n"): + rhs = s[1][:-1] + else: + rhs = s[1] + if rhs == "DO_NOT_MUTATE": + ignoreRules.append(lhs) + elif rhs == "SKIP_MUTATING_REST": + skipRules.append(lhs) + else: + rules.append(((lhs, rhs), (r, ruleSource + ":" + str(ruleLineNo)))) + + return (rules, ignoreRules, skipRules) + + + +def mutants_comby(source, ruleFiles=None, mutateTestCode=False, mutateBoth=False, + ignorePatterns=None, ignoreStringOnly=False, fuzzing=False, language=".generic"): + if ruleFiles is None: + ruleFiles = ["universal.rules"] + comby = Comby() + print("MUTATING WITH RULES (COMBY):", ", ".join(ruleFiles)) + (rules, ignoreRules, _) = parseRules(ruleFiles, True) + for lhs in ignorePatterns: + ignoreRules.append(lhs) + source = ''.join(source) + mutants = [] + + # Lines that match with DO_NOT_MUTATE and other ignore rules will be skipped + ignoreLines = set() + for lhs in ignoreRules: + for match in comby.matches(source, lhs, language=language): + lineRange = (match.location.start.line, match.location.stop.line) + for line in range(lineRange[0],lineRange[1]+1): + ignoreLines.add(line) + + # Instead of line-by-line x rule-by-rule iterate rule-by-rule x match-by-match. + for ((lhs, rhs), ruleUsed) in rules: + try: + for match in comby.matches(source, lhs, language=language): + environment = {} + for entry in match.environment: + environment[entry] = match.environment.get(entry).fragment + mutant = comby.substitute(rhs, environment) + substitutionRange = (match.location.start.offset, match.location.stop.offset) + lineRange = (match.location.start.line, match.location.stop.line) + + # Check if the match contains an ignoreLine + skipMutant = False + for line in range(lineRange[0],lineRange[1]+1): + if line in ignoreLines: + skipMutant = True + break + + if not skipMutant: + mutants.append((substitutionRange, mutant, ruleUsed, lineRange, (lhs,rhs))) + except JSONDecodeError: + continue + except Exception as e: + print(f"WARNING: Got exception {e} running rule {ruleUsed}") + continue + return mutants + +def mutants_regexp(source, ruleFiles=None, mutateTestCode=False, mutateBoth=False, + ignorePatterns=None, ignoreStringOnly=False, fuzzing=False): + if ruleFiles is None: + ruleFiles = ["universal.rules"] + print("MUTATING WITH RULES:", ", ".join(ruleFiles)) + + (rules, ignoreRules, skipRules) = parseRules(ruleFiles) + + for p in ignorePatterns: + try: + lhs = re.compile(p) + except BaseException: + print("*" * 60) + print("FAILED TO COMPILE IGNORE PATTERN:", p) + print("*" * 60) + continue + ignoreRules.append(lhs) + + mutants = [] + produced = {} + lineno = 0 + stringSkipped = 0 + inTestCode = False + targetLine = None + if fuzzing: + # Pick a random target line, ignore others + if len(source) == 0: + return [] + targetLine = random.randrange(1, len(source) + 1) + for l in source: + lineno += 1 + if fuzzing and (lineno != targetLine): + continue + if inTestCode: + if "@END_TEST_CODE" in l: + inTestCode = False + if (not mutateTestCode) and (not mutateBoth): + continue + else: + if "@BEGIN_TEST_CODE" in l: + inTestCode = True + continue + if mutateTestCode and (not mutateBoth): + continue + skipLine = False + for lhs in ignoreRules: + if lhs.search(l): + skipLine = True + break + if skipLine: + continue + abandon = False + for ((lhs, rhs), ruleUsed) in rules: + skipPos = len(l) + for skipRule in skipRules: + skipp = skipRule.search(l, 0) + if skipp and (skipp.start() < skipPos): + skipPos = skipp.start() + + + p = lhs.search(l, 0) + + # skip mutating if match occurs at index >= skipPos + while p and (p.start() < skipPos): + try: + mutant = l[:p.start()] + lhs.sub(rhs, l[p.start():], count=1) + except KeyboardInterrupt: + raise + except Exception as e: + print("WARNING: Applying mutation raised an exception:", e) + print("Abandoning mutation of line", lineno) + abandon = True + break + if mutant[-1] != "\n": + mutant += "\n" + skipDueToString = False + if ignoreStringOnly: + noStringsOrig = "" + inString = False + slen = 0 + for spos in range(0, len(l)): + if not inString: + noStringsOrig += l[spos] + if l[spos] == '"': + inString = True + slen = 0 + else: + slen += 1 + if l[spos] == '"': + noStringsOrig += str(slen > 2) + noStringsOrig += l[spos] + inString = False + noStringsMutant = "" + inString = False + slen = 0 + for spos in range(0, len(mutant)): + if not inString: + noStringsMutant += mutant[spos] + if mutant[spos] == '"': + inString = True + slen = 0 + else: + slen += 1 + if mutant[spos] == '"': + noStringsMutant += str(slen > 2) + noStringsMutant += mutant[spos] + inString = False + if noStringsOrig == noStringsMutant: + skipDueToString = True + stringSkipped += 1 + if (mutant != l) and ((lineno, mutant) not in produced) and (not skipDueToString): + mutants.append((lineno, mutant, ruleUsed, (lhs,rhs))) + produced[(lineno, mutant)] = True + + p = lhs.search(l, p.start()+1) #search from the next position of the current match + if abandon: + break + + if stringSkipped > 0: + print("SKIPPED", stringSkipped, "MUTANTS ONLY CHANGING STRING LITERALS") + return mutants + + +def makeMutant(source, mutant, path): + lineModified = mutant[0] + newCode = mutant[1] + if source[lineModified - 1] == newCode: + print("** WARNING: SKIPPING GENERATING IDENTICAL MUTANT **") + print(mutant) + return False + with open(path, 'w') as file: + lineno = 0 + for l in source: + lineno += 1 + if lineno != lineModified: + file.write(l) + else: + file.write(newCode) + return True + +def makeMutantComby(source, mutant, path): + sourceBeforeFragment = source[:mutant[0][0]] + sourceAfterFragment = source[mutant[0][1]:] + mutantSource = sourceBeforeFragment + mutant[1] + sourceAfterFragment + if mutantSource == source: + print("** WARNING: SKIPPING GENERATING IDENTICAL MUTANT **") + print(mutant) + return False + with open(path, 'w') as file: + file.write(mutantSource) + return True diff --git a/build/lib/universalmutator/prioritize.py b/build/lib/universalmutator/prioritize.py new file mode 100644 index 0000000..003efa4 --- /dev/null +++ b/build/lib/universalmutator/prioritize.py @@ -0,0 +1,131 @@ +from __future__ import print_function +import glob +import sys + +from universalmutator import utils + + +def main(): + + args = sys.argv + + if ("--help" in args) or (len(sys.argv) < 3): + if len(sys.argv) < 3: + print("ERROR: prioritize_mutants requires at least two arguments\n") + print("USAGE: prioritize_mutants [N] [--cutoff ]", end="") + print("[--mutantDir ] [--sourceDir ]") + print(" --verbose: produce verbose output") + print(" --noSDPriority: do not prioritize statement deletions over other mutants") + print(" --mutantDir: directory with all mutants; defaults to current directory") + print(" --sourceDir: directory of source files; defaults to current directory") + print(" --cutoff: if minimum distance is less than , stop") + sys.exit(0) + + infile = sys.argv[1] + outfile = sys.argv[2] + + infiles = glob.glob(infile) + + verbose = False + if "--verbose" in args: + args.remove("--verbose") + verbose = True + + noSDPriority = False + if "--noSDPriority" in args: + args.remove("--noSDPriority") + noSDPriority = True + + mdir = "." + try: + mdirpos = args.index("--mutantDir") + except ValueError: + mdirpos = -1 + + if mdirpos != -1: + mdir = args[mdirpos + 1] + args.remove("--mutantDir") + args.remove(mdir) + if mdir[-1] != "/": + mdir += "/" + + sdir = "." + try: + sdirpos = args.index("--sourceDir") + except ValueError: + sdirpos = -1 + + if sdirpos != -1: + sdir = args[sdirpos + 1] + args.remove("--sourceDir") + args.remove(sdir) + if sdir[-1] != "/": + sdir += "/" + + cutoff = 0.0 + try: + cutoffpos = args.index("--cutoff") + except ValueError: + cutoffpos = -1 + + if cutoffpos != -1: + cutoff = args[cutoffpos + 1] + args.remove("--cutoff") + args.remove(cutoff) + cutoff = float(cutoff) + + N = -1 + if len(args) >= 4: + N = int(args[3]) + + mutants = [] + for f in infiles: + with open(f, 'r') as mfile: + for line in mfile: + name = line[:-1] + suffix = "." + name.split(".")[-1] + mpart = ".mutant." + name.split(".mutant.")[1] + original = name.replace(mpart, suffix) + try: + mutants.append(utils.readMutant(name, original, mutantDir=mdir, sourceDir=sdir)) + except AssertionError: + print("WARNING:", name, "AND SOURCE ARE IDENTICAL") + print("READ", len(mutants), "MUTANTS") + + if N == -1: + N = len(mutants) + + sdmutants = [] + if not noSDPriority: + print("IDENTIFYING STATEMENT DELETION MUTANTS") + sdmutants = list(filter(utils.isStatementDeletion, mutants)) + for m in sdmutants: + mutants.remove(m) + print("PRIORITIZING", len(sdmutants), "STATEMENT DELETIONS") + if len(sdmutants) > 0: + ranking = utils.FPF(sdmutants, N, cutoff=cutoff, verbose=verbose, mutantDir=mdir, + sourceDir=sdir) + with open(outfile, 'w') as outf: + for (m, _) in ranking: + mname = m[0] + outf.write(mname + "\n") + else: + with open(outfile, 'w') as outf: + pass + print() + else: + with open(outfile, 'w') as outf: + pass + + print("PRIORITIZING", int(N) - len(sdmutants), "MUTANTS") + + ranking = utils.FPF(mutants, N - len(sdmutants), cutoff=cutoff, verbose=verbose, avoid=sdmutants, + mutantDir=mdir, sourceDir=sdir) + with open(outfile, 'a') as outf: + for (m, _) in ranking: + mname = m[0] + outf.write(mname + "\n") + + +if __name__ == '__main__': + main() diff --git a/build/lib/universalmutator/prune.py b/build/lib/universalmutator/prune.py new file mode 100644 index 0000000..9a81009 --- /dev/null +++ b/build/lib/universalmutator/prune.py @@ -0,0 +1,136 @@ +from __future__ import print_function +import re +import sys + +from universalmutator import utils + + +def main(): + + args = sys.argv + + if ("--help" in args) or (len(sys.argv) < 2): + if len(sys.argv) < 2: + print("ERROR: show_mutants requires at least one argument\n") + print("USAGE: prune_mutants [--mutantDir ] [--sourceDir ]") + print(" --mutantDir: directory with all mutants; defaults to current directory") + print(" --sourceDir: directory of source files; defaults to current directory") + sys.exit(0) + + infile = sys.argv[1] + outfile = sys.argv[2] + config = sys.argv[3] + + mdir = "." + try: + mdirpos = args.index("--mutantDir") + except ValueError: + mdirpos = -1 + + if mdirpos != -1: + mdir = args[mdirpos + 1] + args.remove("--mutantDir") + args.remove(mdir) + if mdir[-1] != "/": + mdir += "/" + + sdir = "." + try: + sdirpos = args.index("--sourceDir") + except ValueError: + sdirpos = -1 + + if sdirpos != -1: + sdir = args[sdirpos + 1] + args.remove("--sourceDir") + args.remove(sdir) + if sdir[-1] != "/": + sdir += "/" + + mutants = [] + with open(infile, 'r') as mfile: + for line in mfile: + name = line[:-1] + suffix = "." + name.split(".")[-1] + mpart = ".mutant." + name.split(".mutant.")[1] + original = sdir + name.replace(mpart, suffix) + mutants.append(utils.readMutant(name, original, mutantDir=mdir)) + print("READ", len(mutants), "MUTANTS") + + constraints = {} + constraints["orig"] = [] + constraints["mutant"] = [] + constraints["change"] = [] + constraints["function"] = [] + constraints["contract"] = [] + constraints["source"] = [] + constraints["line"] = [] + for v in constraints.keys(): + constraints["!" + v] = [] + for v in constraints.keys(): + constraints[v + "_RE"] = [] + + baseTypes = set() + + with open(config, 'r') as cfile: + for line in cfile: + r = line.rstrip('\n') + (ctype, crule) = r.split(": ", 1) + if ctype not in constraints: + print("INVALID CONSTRAINT TYPE IN PRUNING RULE:", line) + if "_RE" in ctype: + crule = re.compile(crule) + baseTypes.add(ctype.replace("_RE", "").replace("!", "")) + constraints[ctype].append(crule) + + pruned = [] + + properties = {} + for m in mutants: + prunedYet = False + (mfile, sourcefile, pos, orig, mutant) = m + properties["orig"] = orig + properties["mutant"] = mutant + properties["line"] = pos + properties["source"] = sourcefile + if "change" in baseTypes: + properties["change"] = utils.change(m) + if "function" in baseTypes: + properties["function"] = utils.solidityFunction(m) + if "contract" in baseTypes: + properties["contract"] = utils.solidityContract(m) + for ctype in constraints: + field = ctype.replace("_RE", "").replace("!", "") + negated = ctype[0] == "!" + regexp = "_RE" in ctype + for c in constraints[ctype]: + if "line" in ctype: + cstart = int(c.split("-")[0]) + cend = int(c.split("-")[1]) + matched = (properties[field] >= cstart) and (properties[field] <= cend) + elif not regexp: + matched = c in properties[field] + else: + matched = regexp.search(c, properties[field]) + result = not ((matched is None) or (matched is False)) + if negated: + result = not result + if result: + print("=" * 80) + print("PRUNING MUTANT DUE TO RULE:", ctype + ":", c) + utils.show(m) + pruned.append(mfile) + prunedYet = True + break + if prunedYet: + break + print("=" * 80) + print("PRUNED", len(pruned), "MUTANTS") + with open(outfile, 'w') as newmfile: + for m in mutants: + if m[0] not in pruned: + newmfile.write(m[0] + "\n") + + +if __name__ == '__main__': + main() diff --git a/build/lib/universalmutator/python_handler.py b/build/lib/universalmutator/python_handler.py new file mode 100644 index 0000000..fedb8db --- /dev/null +++ b/build/lib/universalmutator/python_handler.py @@ -0,0 +1,85 @@ +import marshal +import os +import sys +import py_compile +import uuid + + +def buildCode(c): + val = [] + try: + val.append(c.co_code) + except BaseException: + if isinstance(c, str): + val.append("@STRING@:" + c) + else: + val.append(c) + try: + for cc in c.co_consts: + bc = buildCode(cc) + try: + if bc[0].find("@STRING@") == 0: + bc = bc[1:] + except BaseException: + pass + val.append(bc) + except BaseException: + pass + return tuple(val) + + +def getPythonCode(fname): + # from https://stackoverflow.com/questions/32562163/how-can-i-understand-a-pyc-file-content + header_sizes = [ + # (size, first version this applies to) + # pyc files were introduced in 0.9.2 way, way back in June 1991. + (8, (0, 9, 2)), # 2 bytes magic number, \r\n, 4 bytes UNIX timestamp + (12, (3, 6)), # added 4 bytes file size + # bytes 4-8 are flags, meaning of 9-16 depends on what flags are set + # bit 0 not set: 9-12 timestamp, 13-16 file size + # bit 0 set: 9-16 file hash (SipHash-2-4, k0 = 4 bytes of the file, k1 = 0) + (16, (3, 7)), # inserted 4 bytes bit flag field at 4-8 + # future version may add more bytes still, at which point we can extend + # this table. It is correct for Python versions up to 3.9 + ] + header_size = next(s for s, v in reversed(header_sizes) if sys.version_info >= v) + + with open(fname, "rb") as f: + _ = f.read(header_size) # first header_size bytes are metadata + try: + code = marshal.load(f) # rest is a marshalled code object + except BaseException: + print("WARNING: UNABLE TO MARSHAL CODE FROM PYC FILE!") + return uuid.uuid4() + if "code" not in str(type(code)): + print("WARNING: INVALID CODE OBJECT READ FROM PYC FILE!") + return uuid.uuid4() + b = buildCode(code) + return b + + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + compiled = tmpMutantName.replace(".py", ".pyc") + + if os.path.exists(compiled): + os.remove(compiled) + + try: + py_compile.compile(tmpMutantName, doraise=True, cfile=compiled) + except py_compile.PyCompileError: + return "INVALID" + + if len(uniqueMutants) == 0: + sourceCompiled = sourceFile.replace(".py", ".pyc") + py_compile.compile(sourceFile, cfile=sourceCompiled) + if os.path.exists(sourceCompiled): + uniqueMutants[getPythonCode(sourceCompiled)] = 1 + + if os.path.exists(compiled): + code = getPythonCode(compiled) + if code in uniqueMutants: + uniqueMutants[code] += 1 + return "REDUNDANT" + uniqueMutants[code] = 1 + return "VALID" + return "INVALID" diff --git a/build/lib/universalmutator/r_handler.py b/build/lib/universalmutator/r_handler.py new file mode 100644 index 0000000..2222de5 --- /dev/null +++ b/build/lib/universalmutator/r_handler.py @@ -0,0 +1,4 @@ +dumb = True + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + return "VALID" diff --git a/build/lib/universalmutator/rust_handler.py b/build/lib/universalmutator/rust_handler.py new file mode 100644 index 0000000..980aea6 --- /dev/null +++ b/build/lib/universalmutator/rust_handler.py @@ -0,0 +1,28 @@ +import os +import subprocess +import shutil + + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + backupName = tmpMutantName + ".backup." + str(os.getpid()) + outName = ".um.mutant_output." + str(os.getpid()) + if len(uniqueMutants) == 0: + shutil.copy(tmpMutantName, backupName) + shutil.copy(sourceFile, tmpMutantName) + with open(outName, 'w') as file: + r = subprocess.call(["rustc", tmpMutantName], + stdout=file, stderr=file) + with open(tmpMutantName.replace(".rs", ""), 'rb') as file: + uniqueMutants[file.read()] = 1 + shutil.copy(backupName, tmpMutantName) + with open(outName, 'w') as file: + r = subprocess.call(["rustc", tmpMutantName], stdout=file, stderr=file) + if r == 0: + with open(tmpMutantName.replace(".rs", ""), 'rb') as file: + code = file.read() + if code in uniqueMutants: + uniqueMutants[code] += 1 + return "REDUNDANT" + uniqueMutants[code] = 1 + return "VALID" + return "INVALID" diff --git a/build/lib/universalmutator/show.py b/build/lib/universalmutator/show.py new file mode 100644 index 0000000..b63bd68 --- /dev/null +++ b/build/lib/universalmutator/show.py @@ -0,0 +1,71 @@ +from __future__ import print_function +import sys + +from universalmutator import utils + + +def main(): + + args = sys.argv + + if ("--help" in args) or (len(sys.argv) < 2): + if len(sys.argv) < 2: + print("ERROR: show_mutants requires at least one argument\n") + print("USAGE: show_mutants [--mutantDir ] [--sourceDir ]") + print(" --mutantDir: directory with all mutants; defaults to current directory") + print(" --sourceDir: directory of source files; defaults to current directory") + print(" --concise: display in concise mutant format") + sys.exit(0) + + infile = sys.argv[1] + + concise = "--concise" in sys.argv + if concise: + args.remove("--concise") + + mdir = "." + try: + mdirpos = args.index("--mutantDir") + except ValueError: + mdirpos = -1 + + if mdirpos != -1: + mdir = args[mdirpos + 1] + args.remove("--mutantDir") + args.remove(mdir) + if mdir[-1] != "/": + mdir += "/" + + sdir = "." + try: + sdirpos = args.index("--sourceDir") + except ValueError: + sdirpos = -1 + + if sdirpos != -1: + sdir = args[sdirpos + 1] + args.remove("--sourceDir") + args.remove(sdir) + if sdir[-1] != "/": + sdir += "/" + + mutants = [] + with open(infile, 'r') as mfile: + for line in mfile: + name = line[:-1] + suffix = "." + name.split(".")[-1] + mpart = ".mutant." + name.split(".mutant.")[1] + original = sdir + name.replace(mpart, suffix) + mutants.append(utils.readMutant(name, original, mutantDir=mdir)) + print("READ", len(mutants), "MUTANTS") + + pos = 1 + for m in mutants: + print("*"*80) + print("MUTANT #" + str(pos) + ":") + pos += 1 + utils.show(m, concise=concise, mutantDir=mdir) + + +if __name__ == '__main__': + main() diff --git a/build/lib/universalmutator/solidity_handler.py b/build/lib/universalmutator/solidity_handler.py new file mode 100644 index 0000000..766b94f --- /dev/null +++ b/build/lib/universalmutator/solidity_handler.py @@ -0,0 +1,59 @@ +import os +import subprocess +import shutil + + +def extractASM(text, filename): + newText = "" + lines = text.split("\n") + assemblyStart = False + for l in lines: + if assemblyStart: + if (filename not in l) and ("auxdata: 0x" not in l): + newText += (l + "\n") + elif l.find("EVM assembly:") == 0: + assemblyStart = True + return newText + + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants, compileFile=None): + copyForImport = False + if compileFile is None: + compileFile = tmpMutantName + else: + shutil.copy(sourceFile, ".um.out." + str(os.getpid()) + ".src_backup") + copyForImport = True + outName = ".um.out." + str(os.getpid()) + ".asm" + if len(uniqueMutants) == 0: + shutil.copy(tmpMutantName, tmpMutantName + ".backup." + str(os.getpid())) + shutil.copy(sourceFile, tmpMutantName) + with open(outName, 'w') as file: + r = subprocess.call( + ["solc", compileFile, "--asm", "--optimize"], stdout=file, stderr=file) + with open(outName, 'r') as file: + if not copyForImport: + uniqueMutants[extractASM(file.read(), tmpMutantName)] = 1 + else: + uniqueMutants[extractASM(file.read(), sourceFile)] = 1 + shutil.copy(tmpMutantName + ".backup." + str(os.getpid()), tmpMutantName) + if copyForImport: + shutil.copy(tmpMutantName, sourceFile) + try: + with open(outName, 'w') as file: + r = subprocess.call(["solc", compileFile, "--asm", + "--optimize"], stdout=file, stderr=file) + finally: + if copyForImport: + shutil.copy(".um.out." + str(os.getpid()) + ".src_backup", sourceFile) + if r == 0: + with open(outName, 'r') as file: + if not copyForImport: + code = extractASM(file.read(), tmpMutantName) + else: + code = extractASM(file.read(), sourceFile) + if code in uniqueMutants: + uniqueMutants[code] += 1 + return "REDUNDANT" + uniqueMutants[code] = 1 + return "VALID" + return "INVALID" diff --git a/build/lib/universalmutator/static/c.rules b/build/lib/universalmutator/static/c.rules new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/universalmutator/static/c_like.rules b/build/lib/universalmutator/static/c_like.rules new file mode 100644 index 0000000..a6a989d --- /dev/null +++ b/build/lib/universalmutator/static/c_like.rules @@ -0,0 +1,20 @@ +#include ==> DO_NOT_MUTATE +(^\s*)(\S+[^{}]+.*)\n ==> \1/*\2*/\n +if (\(.*\)) ==> if (!\1) +if(\(.*\)) ==> if (!\1) +if (\(.*\)) ==> if (0==1) +if(\(.*\)) ==> if(0==1) +if (\(.*\)) ==> if (1==1) +if(\(.*\)) ==> if(1==1) +while (\(.*\)) ==> while (!\1) +while(\(.*\)) ==> while(!\1) +else ==> +\|\|.*\) ==> || (0==1)) +\|\|.*\s ==> || (0==1) +\(.*\|\| ==> ((0==1) || +\s.*\|\| ==> (0==1) || +&&.*\) ==> && (1==1)) +&&.*\s ==> && (1==1) +\(.*&& ==> ((1==1) && +\s.*&& ==> (1==1) && +// ==> SKIP_MUTATING_REST \ No newline at end of file diff --git a/build/lib/universalmutator/static/cpp.rules b/build/lib/universalmutator/static/cpp.rules new file mode 100644 index 0000000..73f0c60 --- /dev/null +++ b/build/lib/universalmutator/static/cpp.rules @@ -0,0 +1,14 @@ +any_of ==> all_of +any_of ==> none_of +all_of ==> none_of +all_of ==> any_of +none_of ==> any_of +none_of ==> all_of +at(\(.*\)) ==> at(0) +at (\(.*\)) ==> at (0) +front_inserter ==> back_inserter +back_inserter ==> front_inserter +min_element ==> max_element +max_element ==> min_element +accumulate ==> inner_product +inner_product ==> accumulate \ No newline at end of file diff --git a/build/lib/universalmutator/static/fe.rules b/build/lib/universalmutator/static/fe.rules new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/universalmutator/static/fortran.rules b/build/lib/universalmutator/static/fortran.rules new file mode 100644 index 0000000..d5eba99 --- /dev/null +++ b/build/lib/universalmutator/static/fortran.rules @@ -0,0 +1,112 @@ +\*\* ==> + +\*\* ==> - +\*\* ==> / + +\+ ==> ** +- ==> ** +\* ==> ** +([^\*/])/([^\*/]) ==> \1**\2 + +\.lt\. ==> .eq. +\.lt\. ==> .ne. +\.lt\. ==> .le. +\.lt\. ==> .gt. +\.lt\. ==> .ge. + +\.le\. ==> .eq. +\.le\. ==> .ne. +\.le\. ==> .lt. +\.le\. ==> .gt. +\.le\. ==> .ge. + +\.gt\. ==> .eq. +\.gt\. ==> .ne. +\.gt\. ==> .le. +\.gt\. ==> .lt. +\.gt\. ==> .ge. + +\.ge\. ==> .eq. +\.ge\. ==> .ne. +\.ge\. ==> .le. +\.ge\. ==> .gt. +\.ge\. ==> .lt. + +\.eq\. ==> .ge. +\.ge\. ==> .ne. +\.ge\. ==> .le. +\.ge\. ==> .gt. +\.ge\. ==> .lt. + +\.ne\. ==> .ge. +\.ne\. ==> .eq. +\.ne\. ==> .le. +\.ne\. ==> .gt. +\.ne\. ==> .lt. + +\.and\. ==> .or. +\.and\. ==> .eqv. +\.and\. ==> .neqv. + +\.or\. ==> .and. +\.or\. ==> .eqv. +\.or\. ==> .neqv. + +\.eqv\. ==> .or. +\.eqv\. ==> .and. +\.eqv\. ==> .neqv. + +\.neqv\. ==> .or. +\.neqv\. ==> .and. +\.neqv\. ==> .eqv + +\.LT\. ==> .EQ. +\.LT\. ==> .NE. +\.LT\. ==> .LE. +\.LT\. ==> .GT. +\.LT\. ==> .GE. + +\.LE\. ==> .EQ. +\.LE\. ==> .NE. +\.LE\. ==> .LT. +\.LE\. ==> .GT. +\.LE\. ==> .GE. + +\.GT\. ==> .EQ. +\.GT\. ==> .NE. +\.GT\. ==> .LE. +\.GT\. ==> .LT. +\.GT\. ==> .GE. + +\.GE\. ==> .EQ. +\.GE\. ==> .NE. +\.GE\. ==> .LE. +\.GE\. ==> .GT. +\.GE\. ==> .LT. + +\.EQ\. ==> .GE. +\.GE\. ==> .NE. +\.GE\. ==> .LE. +\.GE\. ==> .GT. +\.GE\. ==> .LT. + +\.NE\. ==> .GE. +\.NE\. ==> .EQ. +\.NE\. ==> .LE. +\.NE\. ==> .GT. +\.NE\. ==> .LT. + +\.AND\. ==> .OR. +\.AND\. ==> .EQV. +\.AND\. ==> .NEQV. + +\.OR\. ==> .AND. +\.OR\. ==> .EQV. +\.OR\. ==> .NEQV. + +\.EQV\. ==> .OR. +\.EQV\. ==> .AND. +\.EQV\. ==> .NEQV. + +\.NEQV\. ==> .OR. +\.NEQV\. ==> .AND. +\.NEQV\. ==> .EQV \ No newline at end of file diff --git a/build/lib/universalmutator/static/go.rules b/build/lib/universalmutator/static/go.rules new file mode 100644 index 0000000..2c5afe2 --- /dev/null +++ b/build/lib/universalmutator/static/go.rules @@ -0,0 +1,6 @@ +&& .* ==> && true +\|\| .* ==> || false +.* && ==> true && +.* \|\| ==> false || + +defer ==> \ No newline at end of file diff --git a/build/lib/universalmutator/static/java.rules b/build/lib/universalmutator/static/java.rules new file mode 100644 index 0000000..5fba30f --- /dev/null +++ b/build/lib/universalmutator/static/java.rules @@ -0,0 +1,12 @@ +(^\s*import) ==> DO_NOT_MUTATE +(^\s*(@.+)) ==> DO_NOT_MUTATE +(^\s*(\/\*.+)) ==> DO_NOT_MUTATE +(^\s*(\/\/.+)) ==> DO_NOT_MUTATE +(^\s*(\*.+)) ==> DO_NOT_MUTATE + +synchronized ==> + +&& .* ==> && true +\|\| .* ==> || false +.* && ==> true && +.* \|\| ==> false || diff --git a/build/lib/universalmutator/static/javascript.rules b/build/lib/universalmutator/static/javascript.rules new file mode 100644 index 0000000..124b8f7 --- /dev/null +++ b/build/lib/universalmutator/static/javascript.rules @@ -0,0 +1,6 @@ +&& .* ==> && true +\|\| .* ==> || false +.* && ==> true && +.* \|\| ==> false || + +=== ==> !== \ No newline at end of file diff --git a/build/lib/universalmutator/static/lisp.rules b/build/lib/universalmutator/static/lisp.rules new file mode 100644 index 0000000..33b1038 --- /dev/null +++ b/build/lib/universalmutator/static/lisp.rules @@ -0,0 +1,7 @@ +and ==> or +or ==> and +not ==> +or ==> or T +and ==> and nil +T ==> nil +nil ==> T diff --git a/build/lib/universalmutator/static/none.rules b/build/lib/universalmutator/static/none.rules new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/universalmutator/static/python.rules b/build/lib/universalmutator/static/python.rules new file mode 100644 index 0000000..d4cce05 --- /dev/null +++ b/build/lib/universalmutator/static/python.rules @@ -0,0 +1,43 @@ +(^\s*import) ==> DO_NOT_MUTATE + +if (.*):\n ==> if not (\1):\n +while (.*):\n ==> while not (\1):\n + +continue ==> break +break ==> continue + +if \( ==> if not ( + +while \( ==> while not ( + +and ==> or +or ==> and +and .* ==> and True +or .* ==> or False +.* and ==> True and +.* or ==> False or + +not ==> + +return .*\n ==> return None\n + +(^\s*)(\S+.*\n) ==> \1pass + +// ==> / +/ ==> // + +True ==> False + +\[.+\] ==> [] +\[.*, ==> [ +,.*] ==> ] +{.+} ==> {} +{.*, ==> { +,.*} ==> } + +,.+, ==> , +'.+' ==> '' + +@.* ==> + +# ==> SKIP_MUTATING_REST \ No newline at end of file diff --git a/build/lib/universalmutator/static/r.rules b/build/lib/universalmutator/static/r.rules new file mode 100644 index 0000000..1c69a9e --- /dev/null +++ b/build/lib/universalmutator/static/r.rules @@ -0,0 +1 @@ +# ==> SKIP_MUTATING_REST \ No newline at end of file diff --git a/build/lib/universalmutator/static/rust.rules b/build/lib/universalmutator/static/rust.rules new file mode 100644 index 0000000..26b8bd2 --- /dev/null +++ b/build/lib/universalmutator/static/rust.rules @@ -0,0 +1,2 @@ +true ==> false +false ==> true diff --git a/build/lib/universalmutator/static/solidity.rules b/build/lib/universalmutator/static/solidity.rules new file mode 100644 index 0000000..88e5541 --- /dev/null +++ b/build/lib/universalmutator/static/solidity.rules @@ -0,0 +1,50 @@ +pragma solidity ==> DO_NOT_MUTATE +^import\s ==> DO_NOT_MUTATE +true ==> false +false ==> true +([^u])int ==> \1uint +uint ==> int +([^u])fixed ==> \1ufixed +ufixed ==> fixed +int16 ==> int8 +int32 ==> int16 +int64 ==> int32 +memory ==> storage +storage ==> memory +view ==> pure +pure ==> view +constant ==> pure +payable ==> +private ==> +msg.sender ==> tx.origin +tx.origin ==> msg.sender +ether ==> wei +wei|finney|szabo ==> ether +ether|finney|szabo ==> wei +wei|ether|szao ==> finney +wei|finney|ether ==> szabo +minutes|days|hours|weeks|years ==> seconds +seconds|days|hours|weeks|years ==> minutes +seconds|minutes|hours|weeks|years ==> days +seconds|minutes|days|weeks|years ==> hours +seconds|minutes|days|hours|years ==> weeks +seconds|minutes|days|hours|weeks ==> years +now ==> 0 +block.timestamp ==> 0 +msg.value ==> 0 +msg.value ==> 1 +addmod ==> mulmod +mulmod ==> addmod +(^\s*)(\S+[^{}]+.*)\n ==> selfdestruct(msg.sender); +(^\s*)(\S+[^{}]+.*)\n ==> revert(); +call ==> delegatecall +delegatecall ==> call +call ==> callcode +callcode ==> call +delegatecall ==> callcode +callcode ==> delegatecall +(\S+\s+)(\S+)(\s+) ==> \1\3 +if (\(.*\)) ==> if (false) +if(\(.*\)) ==> if(false) +if (\(.*\)) ==> if (true) +if(\(.*\)) ==> if(true) diff --git a/build/lib/universalmutator/static/swift.rules b/build/lib/universalmutator/static/swift.rules new file mode 100644 index 0000000..800aba3 --- /dev/null +++ b/build/lib/universalmutator/static/swift.rules @@ -0,0 +1,15 @@ +var ==> let + +true ==> false +false ==> true + +\[.+] ==> [] +\[.*, ==> [ +,.*] ==> ] + +,.+, ==> , + +&& .* ==> && true +\|\| .* ==> || false +.* && ==> true && +.* \|\| ==> false || diff --git a/build/lib/universalmutator/static/universal.rules b/build/lib/universalmutator/static/universal.rules new file mode 100644 index 0000000..e3b6d78 --- /dev/null +++ b/build/lib/universalmutator/static/universal.rules @@ -0,0 +1,96 @@ +DO_NOT_MUTATE ==> DO_NOT_MUTATE + +\+ ==> - +\+ ==> * +\+ ==> / +\+ ==> % + +-([^>]) ==> +\1 +-([^>]) ==> *\1 +-([^>]) ==> /\1 +-([^>]) ==> %\1 + +\* ==> + +\* ==> - +\* ==> / +\* ==> % + +([^\*/])/([^\*/]) ==> \1+\2 +([^\*/])/([^\*/]) ==> \1-\2 +([^\*/])/([^\*/]) ==> \1*\2 +([^\*/])/([^\*/]) ==> \1%\2 + +% ==> + +% ==> - +% ==> * +% ==> / + +!= ==> == +!= ==> <= +!= ==> >= +!= ==> > +!= ==> < + +== ==> != +== ==> <= +== ==> >= +== ==> > +== ==> < + +>= ==> == +>= ==> != +>= ==> < +>= ==> > + +<= ==> == +<= ==> != +<= ==> < +<= ==> > + +< ==> > +< ==> == +< ==> <= +< ==> >= +< ==> != + +([^-])> ==> \1< +([^-])> ==> \1== +([^-])> ==> \1>= +([^-])> ==> \1<= +([^-])> ==> \1!= + +\+= ==> =+ +-= ==> =- + +-- ==> ++ +\+\+ ==> -- + +-([^>]) ==> \1 + +(\D)(\d+)(\D) ==> \g<1>0\3 +(\D)(\d+)(\D) ==> \g<1>1\3 +(\D)(\d+)(\D) ==> \g<1>-1\3 +(\D)(\d+)(\D) ==> \1(\2+1)\3 +(\D)(\d+)(\D) ==> \1(\2-1)\3 + +&& ==> || +\|\| ==> && +! ==> + +([^&])&([^&]) ==> \1|\2 +([^|])\|([^|]) ==> \1&\2 + +(^\s*)(\S+.*)\n ==> \1\2\n\1break;\n +(^\s*)(\S+.*)\n ==> \1\2\n\1continue;\n + +".+" ==> "" + +while ==> if + +([,\(\[\{])([^=,\)\}\]]+),([^=,\)\}\]]+)([,\)\]\}]) ==> \1\3,\2\4 +([,\(\[\{])([^,=\)\]\}]+)=([^,=\)\]\}]+),([^,=\)\]\}]+)=([^,=\)\]\}]+)([,\)\]\}]) ==> \1\4=\3,\2=\5\6 + +min ==> max +max ==> min +begin ==> end +end ==> begin diff --git a/build/lib/universalmutator/static/vyper.rules b/build/lib/universalmutator/static/vyper.rules new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/universalmutator/swift_handler.py b/build/lib/universalmutator/swift_handler.py new file mode 100644 index 0000000..956432d --- /dev/null +++ b/build/lib/universalmutator/swift_handler.py @@ -0,0 +1,29 @@ +import os +import subprocess +import shutil + + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + backupName = tmpMutantName + ".backup." + str(os.getpid()) + outName = ".um.mutant.output." + str(os.getpid()) + if len(uniqueMutants) == 0: + shutil.copy(tmpMutantName, backupName) + shutil.copy(sourceFile, tmpMutantName) + with open(outName, 'w') as file: + r = subprocess.call(["swiftc", tmpMutantName], + stdout=file, stderr=file) + with open(tmpMutantName.replace(".swift", ""), 'rb') as file: + uniqueMutants[file.read()] = 1 + shutil.copy(backupName, tmpMutantName) + with open(outName, 'w') as file: + r = subprocess.call(["swiftc", tmpMutantName], + stdout=file, stderr=file) + if r == 0: + with open(tmpMutantName.replace(".swift", ""), 'rb') as file: + code = file.read() + if code in uniqueMutants: + uniqueMutants[code] += 1 + return "REDUNDANT" + uniqueMutants[code] = 1 + return "VALID" + return "INVALID" diff --git a/build/lib/universalmutator/utils.py b/build/lib/universalmutator/utils.py new file mode 100644 index 0000000..d8f0c78 --- /dev/null +++ b/build/lib/universalmutator/utils.py @@ -0,0 +1,221 @@ +from __future__ import print_function +import difflib +import time + +import Levenshtein + + +def solidityContract(m): + (_, sourcefile, pos, _, _) = m + cname = "UNKNOWN" + try: + with open(sourcefile, 'r') as readm: + cpos = 0 + for line in readm: + if ("contract " in line) and (line.split()[0][:2] != "//"): + cname = line.split("contract ")[1] + if ("library " in line) and (line.split()[0][:2] != "//"): + cname = "library:" + line.split("library ")[1] + cpos += 1 + if cpos > pos: + break + except KeyboardInterrupt: + raise + except BaseException: + pass + actualName = "" + for c in cname: + if c.isspace() or (c == "("): + break + actualName += c + return actualName + + +def solidityFunction(m): + (_, sourcefile, pos, _, _) = m + fname = "UNKNOWN" + try: + with open(sourcefile, 'r') as readm: + fpos = 0 + for line in readm: + if ("function " in line) and (line.split()[0][:2] != "//"): + fname = line.split("function ")[1] + fpos += 1 + if fpos > pos: + break + except KeyboardInterrupt: + raise + except BaseException: + pass + actualName = "" + for c in fname: + if c.isspace() or (c == "("): + break + actualName += c + return actualName + + +def show(m, concise=False, mutantDir=None, sourceDir=None): + (mfile, sourcefile, pos, orig, mutant) = m + print(mfile + ": " + sourcefile + ":" + str(pos + 1)) + if sourcefile.split(".")[1] == "sol": + print("Function", solidityFunction(m), "in contract", solidityContract(m)) + if concise: + print(orig, end="") + print(" ==> ", change(m)) + print(mutant, end="") + else: + if mutantDir is not None: + mfile = mutantDir + "/" + mfile + if sourceDir is not None: + sourcefile = sourceDir + "/" + sourcefile + with open(sourcefile, 'r') as ff: + fromLines = ff.readlines() + with open(mfile, 'r') as tf: + toLines = tf.readlines() + diff = difflib.context_diff(fromLines, toLines, "Original", "Mutant") + print(''.join(diff)) + print() + + +def change(m): + (_, _, pos, orig, mutant) = m + eops = Levenshtein.editops(orig, mutant) + blocks = Levenshtein.matching_blocks(eops, orig, mutant) + if len(blocks) > 4: + return mutant[:-1] + keep = ''.join([orig[x[0]:x[0]+x[2]] for x in blocks]) + notKeep = "" + pos = 0 + wasDot = False + for c in range(0, len(orig)): + if orig[c] == keep[pos]: + pos += 1 + if not wasDot: + notKeep += "..." + wasDot = True + else: + notKeep += orig[c] + wasDot = False + notKeep += "==>" + pos = 0 + wasDot = False + for c in range(0, len(mutant)): + if (pos < len(keep)) and mutant[c] == keep[pos]: + pos += 1 + if not wasDot: + notKeep += "..." + wasDot = True + else: + notKeep += mutant[c] + wasDot = False + return notKeep + + +def isStatementDeletion(m): + return change(m) == "...==>.../*...*/..." + + +mdistanceCache = {} + + +def d(m1, m2, changeWeight=5.0, origWeight=0.1, mutantWeight=0.1, codeWeight=0.5, useCache=True): + if m1 == m2: + return 0 + if useCache: + if (m1, m2) in mdistanceCache: + return mdistanceCache[(m1, m2)] + if (m2, m1) in mdistanceCache: + return mdistanceCache[(m2, m1)] + (_, sourcefile1, pos1, orig1, mutant1) = m1 + (_, sourcefile2, pos2, orig2, mutant2) = m2 + md = changeWeight * (1.0 - (Levenshtein.ratio(change(m1), change(m2)))) + md += origWeight * (1.0 - Levenshtein.ratio(orig1, orig2)) + md += mutantWeight * (1.0 - Levenshtein.ratio(mutant1, mutant2)) + if sourcefile1 != sourcefile2: + md += codeWeight + else: + pd = abs(pos1 - pos2) + if pd > 10: + md += codeWeight * 0.5 + else: + md += codeWeight * (0.5 * (pd / 11.0)) + if useCache: + mdistanceCache[(m1, m2)] = md + return md + + +def FPF(mlist, N, f=None, df=d, cutoff=0.0, verbose=True, avoid=None, mutantDir=None, sourceDir=None): + if avoid is None: + avoid = [] + if len(mlist) == 0: + return mlist + start = time.time() + if f is None: + ranking = [(mlist[0], -1)] + else: + maxf = 0 + best = None + for m in mlist: + fm = f(m) + if fm > maxf: + best = m + maxf = fm + ranking = [(best, -1)] + if verbose: + print("*"*80) + show(ranking[0][0], mutantDir=mutantDir, sourceDir=sourceDir) + while (len(ranking) < N) and (len(ranking) < len(mlist)): + best = None + maxMin = -1 + for m1 in mlist: + dmin = -1 + for (m2, _) in ranking: + dm1m2 = df(m1, m2) + if (dm1m2 < dmin) or (dmin == -1): + dmin = dm1m2 + for m2 in avoid: + dm1m2 = df(m1, m2) + if (dm1m2 < dmin) or (dmin == -1): + dmin = dm1m2 + if dmin > maxMin: + best = m1 + maxMin = dmin + if verbose: + print("*"*80) + elapsed = time.time() - start + print("RANKED", len(ranking) + 1, "MUTANTS IN", elapsed, "SECONDS") + show(best, mutantDir=mutantDir, sourceDir=sourceDir) + print("DISTANCE:", maxMin) + ranking.append((best, maxMin)) + if maxMin < cutoff: + break + return ranking + + +def readMutant(mutant, source, mutantDir=None, sourceDir=None): + mfile = mutant + if mutantDir is not None: + mfile = mutantDir + "/" + mfile + sfile = source + if sourceDir is not None: + sfile = sourceDir + "/" + sfile + with open(sfile, 'r') as readSource: + scode = readSource.readlines() + with open(mfile, 'r') as readmfile: + mcode = readmfile.readlines() + pos = 0 + # We expect one location of change, contiguous + diffFound = False + for line in scode: + if line != mcode[pos]: + diffFound = True + break + pos += 1 + mpos = pos + if not diffFound: + if len(mcode) > len(scode): + pos = len(scode)-1 + diffFound = True + assert diffFound, "mutant " + mfile + " and source " + source + " are identical!" + return (mutant, source, pos, scode[pos], mcode[mpos]) diff --git a/build/lib/universalmutator/vyper_handler.py b/build/lib/universalmutator/vyper_handler.py new file mode 100644 index 0000000..024b035 --- /dev/null +++ b/build/lib/universalmutator/vyper_handler.py @@ -0,0 +1,31 @@ +import os +import subprocess +import shutil + + +def extractOpcodes(text, filename): + return text + + +def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): + outName = ".um.out." + str(os.getpid()) + ".opcodes" + if len(uniqueMutants) == 0: + shutil.copy(tmpMutantName, tmpMutantName + ".backup." + str(os.getpid())) + shutil.copy(sourceFile, tmpMutantName) + with open(outName, 'w') as file: + r = subprocess.call( + ["vyper", tmpMutantName, "-f", "opcodes"], stdout=file, stderr=file) + with open(outName, 'r') as file: + uniqueMutants[extractOpcodes(file.read(), tmpMutantName)] = 1 + with open(outName, 'w') as file: + r = subprocess.call(["vyper", tmpMutantName, "-f", + "opcodes"], stdout=file, stderr=file) + if r == 0: + with open(outName, 'r') as file: + code = extractOpcodes(file.read(), tmpMutantName) + if code in uniqueMutants: + uniqueMutants[code] += 1 + return "REDUNDANT" + uniqueMutants[code] = 1 + return "VALID" + return "INVALID" diff --git a/universalmutator.egg-info/PKG-INFO b/universalmutator.egg-info/PKG-INFO new file mode 100644 index 0000000..c907983 --- /dev/null +++ b/universalmutator.egg-info/PKG-INFO @@ -0,0 +1,152 @@ +Metadata-Version: 2.4 +Name: universalmutator +Version: 1.1.13 +Summary: Universal regexp-based mutation tool +Home-page: https://github.com/agroce/universalmutator +License: MIT +Keywords: testing mutation mutation-testing +Classifier: Intended Audience :: Developers +Classifier: Development Status :: 4 - Beta +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: comby +Requires-Dist: python-levenshtein +Requires-Dist: tabulate +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: license-file +Dynamic: requires-dist +Dynamic: summary + +This is a tool based on source-based rewrite of code lines for mutation generation, including +multi-language rules aided by special rules for languages or even projects. Originally, the approach used only regular expressions, +treating code as text. However, there is also a mode that can use the [Comby](https://github.com/comby-tools/comby) tool +for more sophisticated mutation that produces fewer invalid mutants. Regular-expression based mutation works well, in our experience; +comby-aided mutation works even better. The key advantage of either approach is that the tool can probably mutate approximately *any* interesting source code you have, and language changes don't force +rewriting of the mutation tool. To use the comby mode, just make sure comby is installed and add `--comby` when you run `mutate`. + +More information on this project can be found in a [2024 FSE paper](https://agroce.github.io/fse24.pdf), and in the original [2018 ICSE Tool Paper](https://agroce.github.io/icse18t.pdf). + +A [guest blog post](https://blog.trailofbits.com/2019/01/23/fuzzing-an-api-with-deepstate-part-2/) for Trail of Bits shows how to use the universalmutator to help improve a C/C++ API fuzzing effort using [DeepState](https://github.com/trailofbits/deepstate) and libFuzzer. + +The universalmutator has support for extracting coverage information to guide mutation from the [TSTL](https://github.com/agroce/tstl) testing tool for Python. + +HOW TO USE IT +============= + +To use this, you should really just do: + +`pip install universalmutator` + +then + +`mutate --help` + +SIMPLE EXAMPLE USAGE +==================== + +`mutate foo.py` + +or + +`mutate foo.swift` + +should, if you have the appropriate compilers installed, generate a bunch of valid, non-trivially redundant, mutants. + + +A MORE COMPLEX EXAMPLE +====================== + +Sometimes the mutated code needs to be built with a more complicated command than a simple compiler call, and of course you want help discovering which mutants are killed and not killed. For example, to mutate and test mutants for the mandelbrot plotting example included in the PROGRAMMING RUST book (http://shop.oreilly.com/product/0636920040385.do), just do this: + + + git clone https://github.com/ProgrammingRust/mandelbrot + cd mandelbrot + cargo build + target/debug/mandelbrot origmandel.png 1000x750 -1.20,0.35 -1,0.20 + mkdir mutants + mutate src/main.rs --mutantDir mutants --noCheck + analyze_mutants src/main.rs "cargo clean; cargo build; rm mandel.png; target/debug/mandelbrot mandel.png 1000x750 -1.20,0.35 -1,0.20; diff mandel.png origmandel.png" --mutantDir mutants + +(It will go faster if you edit `main.rs` to lower the maximum number of threads used to something like 8, not 90.) At the moment, this won't use any Trivial Compiler Equivalence, but still kills about 60% of the 1000+ mutants. The killed mutant filenames will be in `killed.txt` and the non-killed ones in `not-killed.txt`. + +Working with something like maven is very similar, except you can probably edit the complicated build/clean stuff to just a 'mvn test' or similar. + +CURRENTLY SUPPORTED LANGUAGES +============================= + +The tool will likely mutate other things, if you tell it they are "c" or something, but there is auto-detection based on file ending and specific rule support for: + +``` +C +C++ +Java +JavaScript +Python +Swift +R +Rust +Go +Lisp +Fortran +Solidity +Vyper +Fe +``` + +(the last three are smart contract languages for the Ethereum blockchain). + +All but C, C++, JavaScript, and Go will try, by default, to compile the mutated +file and use TCE to detect redundancy. Of course, build dependencies +may frustrate this process, in which case --noCheck will turn off TCE +and just dump all the mutants in the directory, for pruning using a +real build process. In the long run, we plan to integrate with +standard build systems to avoid this problem, and with automated test +generation systems such as TSTL (https://github.com/agroce/tstl) for +Python or Echidna for Solidity +(https://github.com/trailofbits/echidna). Even now, however, with +`analyze_mutants` it is fairly easy to set up automatic evaluation of +your automated test generator. + +MUTATING SOLIDITY CODE +====================== + +The universalmutator has been most frequently applied to smart +contracts written in the Solidity language. It supports a few special +features that are particularly useful in this context. + +First, +Solidity libraries are often written with only `internal` functions +--- and the compiler will not emit code for such functions if you +compile a library by itself, resulting in no non-redundant mutants. +In order to handle this case, `mutate` can take a `--compile` option +that specifies another file (a contract using the library, or the +tests in question) that is used to check whether mutants are +redundant. + +Second, swapping adjacent lines of code is a seldom-used mutation +operator that is unusually attractive in a Solidity context because +swapping a state-changing operation and a requirement may reveal that +testing is incapable of detecting some +[re-entrancy](https://github.com/crytic/not-so-smart-contracts/tree/master/reentrancy) +vulnerabilities. The testing may notice the absence of the check, but +not a mis-ordering, and these mutants may reveal that. To add code +swaps to your mutations, just add `--swap` to the `mutate` call. Note +that swaps work in any language; they are just particularly appealing +for smart contracts. + +MORE INFORMATON +=============== + +For much more information, again see https://agroce.github.io/icse18t.pdf -- demo/tool paper at ICSE 18 and especially our full FSE 2024 paper -- https://agroce.github.io/fse24.pdf -- the latter discusses the latest version of the tool/approach, and includes a comparison with many other mutation testing tools. + +The aim of this project is partly to see how quickly mutation can be applied to new languages, partly how much the work of a tool can be +offloaded to the compiler / test analysis tools. + +Authors: Alex Groce, Josie Holmes, Darko Marinov, August Shi, Lingming Zhang, Kush Jain, Rijnard van Tonder, Sourav Deb diff --git a/universalmutator.egg-info/SOURCES.txt b/universalmutator.egg-info/SOURCES.txt new file mode 100644 index 0000000..e8ee0fb --- /dev/null +++ b/universalmutator.egg-info/SOURCES.txt @@ -0,0 +1,68 @@ +LICENSE +README.md +setup.py +tests/test_comments_rules.py +tests/test_foo_example.py +universalmutator/__init__.py +universalmutator/analyze.py +universalmutator/c_handler.py +universalmutator/checkcov.py +universalmutator/cpp_handler.py +universalmutator/fe_handler.py +universalmutator/findmissing.py +universalmutator/fortran_handler.py +universalmutator/genmutants.py +universalmutator/go_handler.py +universalmutator/intersect.py +universalmutator/java_handler.py +universalmutator/javascript_handler.py +universalmutator/lisp_handler.py +universalmutator/mutator.py +universalmutator/prioritize.py +universalmutator/prune.py +universalmutator/python_handler.py +universalmutator/r_handler.py +universalmutator/rust_handler.py +universalmutator/show.py +universalmutator/solidity_handler.py +universalmutator/swift_handler.py +universalmutator/utils.py +universalmutator/vyper_handler.py +universalmutator.egg-info/PKG-INFO +universalmutator.egg-info/SOURCES.txt +universalmutator.egg-info/dependency_links.txt +universalmutator.egg-info/entry_points.txt +universalmutator.egg-info/requires.txt +universalmutator.egg-info/top_level.txt +universalmutator/comby/c.rules +universalmutator/comby/c_like.rules +universalmutator/comby/cpp.rules +universalmutator/comby/fortran.rules +universalmutator/comby/go.rules +universalmutator/comby/java.rules +universalmutator/comby/lisp.rules +universalmutator/comby/none.rules +universalmutator/comby/python.rules +universalmutator/comby/r.rules +universalmutator/comby/rust.rules +universalmutator/comby/solidity.rules +universalmutator/comby/swift.rules +universalmutator/comby/universal.rules +universalmutator/comby/vyper.rules +universalmutator/static/c.rules +universalmutator/static/c_like.rules +universalmutator/static/cpp.rules +universalmutator/static/fe.rules +universalmutator/static/fortran.rules +universalmutator/static/go.rules +universalmutator/static/java.rules +universalmutator/static/javascript.rules +universalmutator/static/lisp.rules +universalmutator/static/none.rules +universalmutator/static/python.rules +universalmutator/static/r.rules +universalmutator/static/rust.rules +universalmutator/static/solidity.rules +universalmutator/static/swift.rules +universalmutator/static/universal.rules +universalmutator/static/vyper.rules \ No newline at end of file diff --git a/universalmutator.egg-info/dependency_links.txt b/universalmutator.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/universalmutator.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/universalmutator.egg-info/entry_points.txt b/universalmutator.egg-info/entry_points.txt new file mode 100644 index 0000000..e75b6a1 --- /dev/null +++ b/universalmutator.egg-info/entry_points.txt @@ -0,0 +1,8 @@ +[console_scripts] +analyze_mutants = universalmutator.analyze:main +check_covered = universalmutator.checkcov:main +intersect_mutants = universalmutator.intersect:main +mutate = universalmutator.genmutants:main +prioritize_mutants = universalmutator.prioritize:main +prune_mutants = universalmutator.prune:main +show_mutants = universalmutator.show:main diff --git a/universalmutator.egg-info/requires.txt b/universalmutator.egg-info/requires.txt new file mode 100644 index 0000000..f68a6dc --- /dev/null +++ b/universalmutator.egg-info/requires.txt @@ -0,0 +1,3 @@ +comby +python-levenshtein +tabulate diff --git a/universalmutator.egg-info/top_level.txt b/universalmutator.egg-info/top_level.txt new file mode 100644 index 0000000..132847f --- /dev/null +++ b/universalmutator.egg-info/top_level.txt @@ -0,0 +1 @@ +universalmutator diff --git a/universalmutator/__pycache__/__init__.cpython-312.pyc b/universalmutator/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67ad44b1a92955ec972c0f0db68989fdb6686905 GIT binary patch literal 154 zcmX@j%ge<81dcmjWrFC(AOanHW&w&!XQ*V*Wb|9fP{ah}eFmxdWvO4DUzA;3keHlW ztY4a!S(aK9UeRljRm~Np68}X-Q&UiCMgchF#8r)aeir0a;jTq zPU4*wWv5T2V_7=ei5cXa(qT& zUP?}CkzPUNEp{lo2;@nS`-)kCL<7SOZlTXW27{j_(=C>i(%dAlf+CQeMcg2Q6G;5x ou*uC&Da}c>E8+!mL7`F149UeRljRm~Np68}X-Q&UiCMgchF#8r)aeir0a;jTq zPU4*wWv5T2V_7=ei5cXazR0S zMq*w{PHK@}LFFxWD7y&cO^^$VS%E|Y!wqhs&p-x)pC;2SmXy-mB(Q=akgY}BAc7M} q{Nk|5%}*)KNwq8D1#&^*QOpk{J}@&fGTvb4ZeaP$!pJBL)&KxZJX5m( literal 0 HcmV?d00001 diff --git a/universalmutator/__pycache__/fe_handler.cpython-312.pyc b/universalmutator/__pycache__/fe_handler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1eb71d4c7039fffb0535259325b3553d81b5d26 GIT binary patch literal 2564 zcmd5;O>7fK6rR~1?{4gM?2rUPN@5aJ2q8`p6iS<*1_F)?2~cP&q9SU%>)>E#-Pv^# z8%IS|4@QcFQ4gdlYF+ivR;j97dut(Tkb1G}RB|`D^#rFtBBe?%eY5c@q!sF+s>G~z z=FQA|-@NzD?3?{rma7nyp6?$;_c##xop$WPRD|W1K)8bxq~I7DTyOCpw))HT5{j!j zHS_t_Uh5pw$Td9?3aff&awIyc5j*0aJiir_~Kqc2?gwwG1K(bF{7$N{Hp;Nf?E&u|eIoib2P%GW@hfWo+7REBa~Z zD-5kCID;-}l?*#wnOE3rdk`Xz49T7wwl>4=RrG--g`+6{#gFOAHemEGt1%h0p({PD z?b_@Y?J2w+3*dU~`3$ExY>b!D1aEAyXNGHSX`@vHh)+}`rAl!s{E(30GyJ!%=?c>m z4me)$*mxKz@`PY)we5%NZE2%b+_qP}=+Z%8Uwq0pQK`1EsBam;Xs|6a{BWZ!Z?yKV zZt#+p0aN(9Uz3M8q(;20WbFQPHriuNNNk1=!{!vk}z&zxznXEtH7 z)OPaMXCY30XIpJvY4Wr6@wVE5wCGR9{aVuSgQQgw68$KldnFXUmQ485Zod&vT&Brw z#JC($O+02Y5jE}T=uqQP!(>y*SlZdqp+OQHBT++5GaVYFPKRzN0Bp4?Atp-KZwBhz z+qu^d#wI2YY3?zlH&67wHPG8LaKYr??l~RoHHF}SCA7IsRyT;rXu9bbQjJ7ZFyZ52+wLB?OFmD1lKS*|sjbc9(>PsdK*wO^c%Ymh+}F*Prj6d9~QuJn+yIJynm|n`q^UZ*?IAt zt@1`*o)`T~3@dpTT{XA*Z}u0q@11wGuQ-sqCbxU)KuK<1LX4Ec`Gbp|?RTngSC_qY zxrzMM8GdHs2c>ZO-Gb**Au?Rh$5vSEP2p_MDogd39LQ6Xvunly|h` z`)3Xojs%xjyyFxupMR(Do&t``5uC-~IGDf^qLl6VaI{?Q{XhJ7d2-!Fr?2d4nA%lx zHRtyiU9>H4X(~50S3)aS*O#5Of2^VlNs z&y_0VdI6Mq`8Ux8%DhaKX+RbeYpbpeGS~DAaH{U1xDBVefY~1v>fJ9d&^& zG<($7=VX8KHuY`g=W3)tGdtIWY2CsH_6c*HeBg*Q*Cq9d>;nPQ+R69T3J9UeRljRm~Np68}X-Q&UiCMgchF#8r)aeir0a;jTq zPU4*wWv5T2V_7=ei5cXT7FSU zQDRg!F literal 0 HcmV?d00001 diff --git a/universalmutator/__pycache__/genmutants.cpython-312.pyc b/universalmutator/__pycache__/genmutants.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..095406527a7a5fe41d068973a2b119cd1d69337e GIT binary patch literal 23503 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
  • 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 Tl9xYGFu&gmpCVLLkP`oIR~x+d literal 0 HcmV?d00001 diff --git a/universalmutator/__pycache__/go_handler.cpython-312.pyc b/universalmutator/__pycache__/go_handler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba13a9532df08c5671e0906697577a9f77e53e5b GIT binary patch literal 329 zcmX@j%ge<81dcmjWyS;P#~=<2FhUuh`GAb+3@Hpz3@MB)3{i}gOq$Fg$t*B+P?{M? ze+CIoXQ*V9UeRljRm~Np68}X-Q&UiCMgchF#8r)aeir0a;jTq zPU4*wWv5T2V_7=ei5cXdVYLH zVqQv4YLQ++&Kn8=KCetmJl+xTJu!16xrA6Ezf)hym p;;_lhPbtkwwJYKUazVjS%nu|!Ff%eT-eBi$VEN3#$S4ff007@sQ$+v( literal 0 HcmV?d00001 diff --git a/universalmutator/__pycache__/java_handler.cpython-312.pyc b/universalmutator/__pycache__/java_handler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98e2356a23acbef1b4a759b9b07cc76254a4e181 GIT binary patch literal 2053 zcmds1PiWg#7=KT){#kZxrJ1{>j0UIwoD_#LLPFR)97?F0HG>@r$qq%8w8oAcX-`tz z7=?u~MhydJr{w5kiUV8MK(|}RMlZY6*kwlXZKv%NNTHBj_9RPH%nn=kC%neu8-t&o%#N=D z&$-9;=$<5wZ~_ib!SCb0ngShoqsu2ufyNW_r;L$~&*^1U2srQ=D|1Vi06=gxyvh%Q zE5Lvy0)lw}@$=P}cgcBBg2N}>xBpR3N}vR~Ri`3zWj<{c)3-%w$A;dhx%N_uaA?DnvD%{wLdcz!L*2}Nkh|(IxjBzX zMI6#Fkwa*PL*G$MJuf>9Qgl_66zsTYnR(R-Nt(XugmlqdbZF&n-Y^X(r0L4C6E>{d zI?^P?z#2(ZRfk4OQCm?`oD(&R`VB9+IkAX?brZ!dnuR1~Ca)?^)LPDeYAIbS!wGk5 z_RKl2T+b@zvD02ZytB|Jt}{f2`^^jm>+rp?l7^NH9j9)L;m9irGDOvdH8u3A!fn&7 zMR8eH6_m#HF+Puma8w$<2EyObI%o@Lwu%j5bVu4vK1w}EJsfSOGYuiLe!U$Z{576z z#FMp&?Q6~W_}ZFNRwpVG)l4O`m1^0_RR8J8Eblh-&h6+%2VM(+O? z^|{*+-tL0~yaRL=Z-?x=MNPI;C4+{s=>8ZD*Lt6X5bgnP4+wi8dO#1rH&7Dm{{mbo Bie3N! literal 0 HcmV?d00001 diff --git a/universalmutator/__pycache__/javascript_handler.cpython-312.pyc b/universalmutator/__pycache__/javascript_handler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..473b27c1da29f859128494cccdba99c0482a49d1 GIT binary patch literal 337 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 literal 0 HcmV?d00001 diff --git a/universalmutator/__pycache__/swift_handler.cpython-312.pyc b/universalmutator/__pycache__/swift_handler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bdeb8af9514759defcda1537f8581500c49a8792 GIT binary patch literal 1877 zcmcgtO>7%Q6rTO@{@7lpcA6H~$WD{CVQb-1fg>fvks_r=t%xh2AeswuyxY`{9h=$R zII-4hggCeYsjfK0hyy-wXyhP*KtkNALUZ9#V0X~2q7^m z&-TX^H$#`3{w} z$^xBgSd}Ge;W+@{y2{&Y8Iu{?>Pa2wbi?9>0RIq6>fni3!LFn`q{lU)pvE^zUcen#4++9WC*;hS4RbPU%c$z>eIPHun?A zbpY?_EYbg8=QmjEgB{?8BbtB3gGJ-_Hll(@)TGPR%;A|+o$ z%SCdUnp{*Pc}*>7Y6Rk!)*y-oqZHHE)RagfO_k89DP;&tOvGF=fI-5!hxoHF_}4E? zznPexN?b7)aFuhY*pdvDWELZ- zd3exavhmLK#W3DtiSf!k8E&<^W1XXbX8~S<3Di`+qzU2 zuqAk_BefS>LTDF|{CNGPog*jK`fm5tpScrzFgVs832#n6q6qJJWvWA=fJY4Q_^QwU z>>FwNM(%K%LoHu)Q*8+oyMW-scj>lxwlY&Y^{M!=_({0wKGUI)`*&*t79=lJ=BrAJ z4w==H=Sk_40#fqwV35O&UQ8qZI-{I!1RH!10x-_m=Qp4?zwgc>x{DJ!jxTjQWm08t19+*+6`Ny6^JFPg3_|jyS_^ z@s7BU-5PMjgY4ExNBk@+(hy(35Z{2%hbMfz>tGMf0OG&CE;o_OOIlW*glCY6zYGQ6 ZvAYyO5Zi#?2HtHT>@mlQ0eBku{{)>hl(GN- literal 0 HcmV?d00001 diff --git a/universalmutator/__pycache__/vyper_handler.cpython-312.pyc b/universalmutator/__pycache__/vyper_handler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..425442691913af40ae8202f3cada7aac4e91f437 GIT binary patch literal 1932 zcmcH)TTC2P^xk>w%(6SXAdf&1*ap&eL%VI$HbDbKEK8-8rBzVNkJ;`_DX=f^%m9l+ zN{k;{V^WMCtSO0W{E&tw<-J+a@L{4q^@~W8P4>%khnYfb&=}*{%(>^D zd*1h+vp-5w6$0pc@cYC&974a+jRTBL>>LAP3CT#taWuBO;xTOPnK4FYK15@z%mU^z zILJMrYLS?uO?MbR7QP)9pe1CWH&GBr?xG-La+)%$nd0?{xRQ(}6qTw40+rsAGbu8q zrlT=MJ*g)rrWK+_;|X1hYANzOV>**o$h$Y9Nja{NP&#A!z$%HxwBd9tB`Ycs!7O#I z3V;?+$>U$Rv;!oHLDtWJN?CCR07J%Dqmf&nQN9I}`!#Ids}+|cl*~~p|0Rx9`zA2@2XBml_Q^_4ECwg@4*wjQ=QY1` zGFk0Vd#mh%m<3rJ=M3K9@3~j)y3M=by}-}01kvpGQ04Hc2iaq=TCHQ#;3w-HdT(_q zz87;h9R973|0Ro?F8eMo9GuU;&t+ctYbG}ZgPlgiXRwAa%gi!OG+WOc)G)@z7@B*= zOY3F`7A<9b?dUX8@eG5}RcJtrK7KIEUPUwbIsT^y@RJAd2X^^Ee`HP!>4{KE*Fr$0 z>{g@C4Yt>!u_-+rGVzSbTkSc=oVacZwiI-}OVwmhwN`}?6O$*jJ52W*;r`J`e_v$8 z`^q8!w5tB)&rfXc$(i5`D!rTB8H*{?xZgQ~{^gfqL1FuTeucb)} z=7$RWXgqGs0Fxt1R1S)!q$ScrFiw(M1jdf(qN63LfmTWC^Y4+{4BI?iN4#OTb{+Ce9zj;h4!9xcKy}w2frPBbgB5t zxMmb*`^qd8d+I{I7oI6%_eD@Fy*8K~S`Mw$-EX?vwA#FuC?4uBc*A9u9{=W+vg3l_rI#E99(~qoBOoXa#+%5!iE1{H^oI-M>K(Bj*ISx@0wxTjjQ{`u literal 0 HcmV?d00001 diff --git a/universalmutator/mutator.py b/universalmutator/mutator.py index def4003..b232aeb 100644 --- a/universalmutator/mutator.py +++ b/universalmutator/mutator.py @@ -1,6 +1,6 @@ from __future__ import print_function import re -import pkg_resources +import importlib.resources import random from comby import Comby import os @@ -17,7 +17,7 @@ def parseRules(ruleFiles, comby=False): rulePath = os.path.join('comby', ruleFile) else: rulePath = os.path.join('static', ruleFile) - with pkg_resources.resource_stream('universalmutator', rulePath) as builtInRule: + with importlib.resources.resource_stream('universalmutator', rulePath) as builtInRule: for line in builtInRule: line = line.decode() rulesText.append((line, "builtin:" + ruleFile)) From 442dece5e0a02575f59dd4ac5ae1c1a83f634f82 Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Thu, 23 Apr 2026 18:00:57 -0700 Subject: [PATCH 10/18] Full Test File For Comments Fnished --- tests/test_comments_rules.py | 122 ++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 3 deletions(-) diff --git a/tests/test_comments_rules.py b/tests/test_comments_rules.py index 33d5083..4e48f79 100644 --- a/tests/test_comments_rules.py +++ b/tests/test_comments_rules.py @@ -86,6 +86,7 @@ def test_only_comments(self): "# comment\n" " # indented comment\n" " # spaced comment\n" + "#\t tab comment\n" ) self.assertEqual(len(rules), 0) @@ -95,31 +96,146 @@ def test_only_comments(self): # --------------------------------------------------- # TEST 5: Invalid lines should still warn, but not be treated as rules # --------------------------------------------------- + def test_invalid_lines(self): + rules, ignoreRules, skipRules, out = self._parse( + " # comment line\n" + "\t# tab comment\n" + "\\+ ==> -\n" + "# ==> SKIP_MUTATING_REST\n" + "invalid line\n" + ) + self.assertEqual(len(rules), 1) + self.assertEqual(len(ignoreRules), 0) + self.assertEqual(len(skipRules), 1) # --------------------------------------------------- # TEST 6: Mixed file test with comments, blank lines, valid rules, and invalid lines # --------------------------------------------------- + def test_mixed_file(self): + rules, ignoreRules, skipRules, out = self._parse( + "# comment\n" + "\n" + " # indented comment\n" + " # spaced comment\n" + "\\+ ==> -\n" + "invalid line\n" + "# ==> SKIP_MUTATING_REST\n" + ) + self.assertEqual(len(rules), 1) + self.assertEqual(len(ignoreRules), 0) + self.assertEqual(len(skipRules), 1) # --------------------------------------------------- # TEST 7: Disabled rules should be ignored, but still treated as comments # --------------------------------------------------- + def test_disabled_rules_ignored(self): + rules, ignoreRules, skipRules, out = self._parse( + "#DISABLED: \\+ ==> -\n" + "#DISABLED: #include ==> DO_NOT_MUTATE\n" + "#DISABLED: # ==> SKIP_MUTATING_REST\n" + "\\* ==> /\n" + ) + self.assertEqual(len(rules), 1) + self.assertEqual(len(ignoreRules), 0) + self.assertEqual(len(skipRules), 0) # --------------------------------------------------- - # TEST 8: Disabled rules with different spacing should still be ignored (MAYBE?) + # TEST 8: Disabled rules with different spacing should still be ignored (MAYBE?) Might be problems # --------------------------------------------------- + def test_disabled_rules_varied_spacing(self): + rules, ignoreRules, skipRules, out = self._parse( + "\t\t\t#DISABLED: \\+ ==> -\n" + " #DISABLED: #include ==> DO_NOT_MUTATE\n" + "\t#DISABLED: # ==> SKIP_MUTATING_REST\n" + "\\* ==> /\n" + ) + self.assertEqual(len(rules), 1) + self.assertEqual(len(ignoreRules), 0) + self.assertEqual(len(skipRules), 0) # --------------------------------------------------- - # TEST 9: Larger file test with multiple comments, blank lines, valid rules, invalid lines, and disabled rules + # TEST 9: Header Testing with comments, blank lines before and after header, and example rules comments # --------------------------------------------------- + def test_header_with_comments_and_blank_lines(self): + rules, ignoreRules, skipRules, out = self._parse( + "# This is a header comment\n" + "# It should be ignored\n" + "\n" + "# Another header comment\n" + "\n" + "# Example rule comment\n" + "#DISABLED: ==> \n" + "# This is an example rule that should be ignored\n" + "#DISABLED: #include ==> DO_NOT_MUTATE\n" + ) + self.assertEqual(len(rules), 1) + self.assertEqual(len(ignoreRules), 0) + self.assertEqual(len(skipRules), 0) # --------------------------------------------------- - # TEST 10: Header Testing with comments, blank lines before and after header, and example rules comments + # TEST 10: Larger file test with multiple comments, blank lines, valid rules, invalid lines, and disabled rules # --------------------------------------------------- + def test_larger_mixed_file(self): + rules, ignoreRules, skipRules, out = self._parse( + "# =====================================================\n" + "# HEADER COMMENT BLOCK\n" + "# This file tests real-world mixed .rules behavior\n" + "# =====================================================\n" + "\n" + "# Simple arithmetic rules\n" + "\\+ ==> -\n" + "\\- ==> +\n" + "\n" + "# multiplication and division\n" + "\\* ==> /\n" + "\\/ ==> *\n" + "\n" + " # indented comment inside file\n" + "\t# tab-indented comment\n" + "\n" + "# Disabled rule section\n" + "# DISABLED: \\+ ==> *\n" + "# DISABLED: \\* ==> +\n" + "\n" + "# Special ignore rule\n" + "#include ==> DO_NOT_MUTATE\n" + "\n" + "# Special skip rule\n" + "# ==> SKIP_MUTATING_REST\n" + "\n" + "# Invalid lines (should trigger warnings)\n" + "this is not a rule\n" + "==> broken rule\n" + "\\+ == bad format\n" + "\n" + "# More valid rules\n" + "== ==> !=\n" + "!= ==> ==\n" + "< ==> >\n" + "> ==> <\n" + "\n" + "# More noise\n" + "random text here\n" + "another bad line\n" + "\n" + "# Final valid rule\n" + "\\% ==> +\n" + ) + + # Expected valid rules: + # +, -, *, /, ==, !=, <, >, % + self.assertEqual(len(rules), 9) + + # Only one ignore rule (#include ==> DO_NOT_MUTATE) + self.assertEqual(len(ignoreRules), 1) + + # Only one skip rule (# ==> SKIP_MUTATING_REST) + self.assertEqual(len(skipRules), 1) if __name__ == "__main__": unittest.main() \ No newline at end of file From 3ab9107308dd89f8a3a746e9ca4de5fe9274010d Mon Sep 17 00:00:00 2001 From: jag2289-blip Date: Fri, 24 Apr 2026 01:29:44 +0000 Subject: [PATCH 11/18] Changed mutator.py to be able to use the correct libraries depending on the python version being used. Credit to: cr2829 --- universalmutator/mutator.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/universalmutator/mutator.py b/universalmutator/mutator.py index b232aeb..fc0ac68 100644 --- a/universalmutator/mutator.py +++ b/universalmutator/mutator.py @@ -1,11 +1,38 @@ from __future__ import print_function import re -import importlib.resources +import sys import random from comby import Comby import os from json.decoder import JSONDecodeError +# Python 3.9+ has importlib.resources with the modern files() API. +# Older Pythons (< 3.9) fall back to pkg_resources from setuptools. +if sys.version_info >= (3, 9): + import importlib.resources as _importlib_resources + _use_importlib = True +else: + try: + import importlib.resources as _importlib_resources + _use_importlib = True + except ImportError: + import pkg_resources as _pkg_resources + _use_importlib = False + + +def _open_package_resource(package, resource_path): + """Open a package data file, using importlib.resources on Python 3.9+ + and falling back to pkg_resources on older versions.""" + if _use_importlib: + parts = resource_path.replace("\\", "/").split("/") + subpackage = package + "." + ".".join(parts[:-1]) if len(parts) > 1 else package + filename = parts[-1] + ref = _importlib_resources.files(subpackage).joinpath(filename) + return ref.open("rb") + else: + return _pkg_resources.resource_stream(package, resource_path) + + def parseRules(ruleFiles, comby=False): rulesText = [] @@ -17,7 +44,7 @@ def parseRules(ruleFiles, comby=False): rulePath = os.path.join('comby', ruleFile) else: rulePath = os.path.join('static', ruleFile) - with importlib.resources.resource_stream('universalmutator', rulePath) as builtInRule: + with _open_package_resource('universalmutator', rulePath) as builtInRule: for line in builtInRule: line = line.decode() rulesText.append((line, "builtin:" + ruleFile)) From 5c50bdefc699ab5fb93ce7672425edadc60fd82f Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Thu, 23 Apr 2026 19:03:23 -0700 Subject: [PATCH 12/18] Fixed the Commented Rules Statement to now work --- universalmutator/mutator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/universalmutator/mutator.py b/universalmutator/mutator.py index fc0ac68..ff4d0c6 100644 --- a/universalmutator/mutator.py +++ b/universalmutator/mutator.py @@ -20,6 +20,7 @@ _use_importlib = False + def _open_package_resource(package, resource_path): """Open a package data file, using importlib.resources on Python 3.9+ and falling back to pkg_resources on older versions.""" @@ -79,7 +80,7 @@ def parseRules(ruleFiles, comby=False): continue # check for disabled rules - if line.startswith("# DISABLED:"): + if line.startswith("#DISABLED:"): # ignore disabled rules continue From 8888a262107283aef606c8bc06a4d4359a27dc43 Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Thu, 23 Apr 2026 19:05:27 -0700 Subject: [PATCH 13/18] Revert "Fixed the Commented Rules Statement to now work" This reverts commit 5c50bdefc699ab5fb93ce7672425edadc60fd82f. --- universalmutator/mutator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/universalmutator/mutator.py b/universalmutator/mutator.py index ff4d0c6..fc0ac68 100644 --- a/universalmutator/mutator.py +++ b/universalmutator/mutator.py @@ -20,7 +20,6 @@ _use_importlib = False - def _open_package_resource(package, resource_path): """Open a package data file, using importlib.resources on Python 3.9+ and falling back to pkg_resources on older versions.""" @@ -80,7 +79,7 @@ def parseRules(ruleFiles, comby=False): continue # check for disabled rules - if line.startswith("#DISABLED:"): + if line.startswith("# DISABLED:"): # ignore disabled rules continue From eb09b8668fcb41864341dcdfc719495c08d9025a Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Thu, 23 Apr 2026 19:10:14 -0700 Subject: [PATCH 14/18] Fixed Disabled Rule Checker to Work properly and fixes to tests to only allow "#DISABLED:" --- tests/test_comments_rules.py | 6 +++--- universalmutator/mutator.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_comments_rules.py b/tests/test_comments_rules.py index 4e48f79..b5e51b7 100644 --- a/tests/test_comments_rules.py +++ b/tests/test_comments_rules.py @@ -198,9 +198,9 @@ def test_larger_mixed_file(self): " # indented comment inside file\n" "\t# tab-indented comment\n" "\n" - "# Disabled rule section\n" - "# DISABLED: \\+ ==> *\n" - "# DISABLED: \\* ==> +\n" + "#Disabled rule section\n" + "#DISABLED: \\+ ==> *\n" + "#DISABLED: \\* ==> +\n" "\n" "# Special ignore rule\n" "#include ==> DO_NOT_MUTATE\n" diff --git a/universalmutator/mutator.py b/universalmutator/mutator.py index fc0ac68..b4e65b1 100644 --- a/universalmutator/mutator.py +++ b/universalmutator/mutator.py @@ -79,7 +79,7 @@ def parseRules(ruleFiles, comby=False): continue # check for disabled rules - if line.startswith("# DISABLED:"): + if line.startswith("#DISABLED:"): # ignore disabled rules continue From 2ad988760bb642d1630c15fa956222e4d635f841 Mon Sep 17 00:00:00 2001 From: iM4ttt Date: Thu, 23 Apr 2026 19:18:23 -0700 Subject: [PATCH 15/18] Incorrect Test Check Value - Fixed to now expect 0 rules in the header test --- tests/test_comments_rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_comments_rules.py b/tests/test_comments_rules.py index b5e51b7..a0dd076 100644 --- a/tests/test_comments_rules.py +++ b/tests/test_comments_rules.py @@ -173,7 +173,7 @@ def test_header_with_comments_and_blank_lines(self): "#DISABLED: #include ==> DO_NOT_MUTATE\n" ) - self.assertEqual(len(rules), 1) + self.assertEqual(len(rules), 0) self.assertEqual(len(ignoreRules), 0) self.assertEqual(len(skipRules), 0) From 6a6c189f8b55306ea30fd60c260015fe81701d05 Mon Sep 17 00:00:00 2001 From: jag2289-blip Date: Sat, 25 Apr 2026 02:31:21 +0000 Subject: [PATCH 16/18] Deleted folders build and universalmutatoregg not needed. --- build/lib/universalmutator/mutator.py | 33 ++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/build/lib/universalmutator/mutator.py b/build/lib/universalmutator/mutator.py index def4003..b4e65b1 100644 --- a/build/lib/universalmutator/mutator.py +++ b/build/lib/universalmutator/mutator.py @@ -1,11 +1,38 @@ from __future__ import print_function import re -import pkg_resources +import sys import random from comby import Comby import os from json.decoder import JSONDecodeError +# Python 3.9+ has importlib.resources with the modern files() API. +# Older Pythons (< 3.9) fall back to pkg_resources from setuptools. +if sys.version_info >= (3, 9): + import importlib.resources as _importlib_resources + _use_importlib = True +else: + try: + import importlib.resources as _importlib_resources + _use_importlib = True + except ImportError: + import pkg_resources as _pkg_resources + _use_importlib = False + + +def _open_package_resource(package, resource_path): + """Open a package data file, using importlib.resources on Python 3.9+ + and falling back to pkg_resources on older versions.""" + if _use_importlib: + parts = resource_path.replace("\\", "/").split("/") + subpackage = package + "." + ".".join(parts[:-1]) if len(parts) > 1 else package + filename = parts[-1] + ref = _importlib_resources.files(subpackage).joinpath(filename) + return ref.open("rb") + else: + return _pkg_resources.resource_stream(package, resource_path) + + def parseRules(ruleFiles, comby=False): rulesText = [] @@ -17,7 +44,7 @@ def parseRules(ruleFiles, comby=False): rulePath = os.path.join('comby', ruleFile) else: rulePath = os.path.join('static', ruleFile) - with pkg_resources.resource_stream('universalmutator', rulePath) as builtInRule: + with _open_package_resource('universalmutator', rulePath) as builtInRule: for line in builtInRule: line = line.decode() rulesText.append((line, "builtin:" + ruleFile)) @@ -52,7 +79,7 @@ def parseRules(ruleFiles, comby=False): continue # check for disabled rules - if line.startswith("# DISABLED:"): + if line.startswith("#DISABLED:"): # ignore disabled rules continue From 0abe043132498e2dff9a2a7f9d258dd2b1a2002d Mon Sep 17 00:00:00 2001 From: jag2289-blip Date: Sat, 25 Apr 2026 02:33:25 +0000 Subject: [PATCH 17/18] Deleted Folder universalmutator.egg not needed --- universalmutator.egg-info/PKG-INFO | 152 ------------------ universalmutator.egg-info/SOURCES.txt | 68 -------- .../dependency_links.txt | 1 - universalmutator.egg-info/entry_points.txt | 8 - universalmutator.egg-info/requires.txt | 3 - universalmutator.egg-info/top_level.txt | 1 - 6 files changed, 233 deletions(-) delete mode 100644 universalmutator.egg-info/PKG-INFO delete mode 100644 universalmutator.egg-info/SOURCES.txt delete mode 100644 universalmutator.egg-info/dependency_links.txt delete mode 100644 universalmutator.egg-info/entry_points.txt delete mode 100644 universalmutator.egg-info/requires.txt delete mode 100644 universalmutator.egg-info/top_level.txt diff --git a/universalmutator.egg-info/PKG-INFO b/universalmutator.egg-info/PKG-INFO deleted file mode 100644 index c907983..0000000 --- a/universalmutator.egg-info/PKG-INFO +++ /dev/null @@ -1,152 +0,0 @@ -Metadata-Version: 2.4 -Name: universalmutator -Version: 1.1.13 -Summary: Universal regexp-based mutation tool -Home-page: https://github.com/agroce/universalmutator -License: MIT -Keywords: testing mutation mutation-testing -Classifier: Intended Audience :: Developers -Classifier: Development Status :: 4 - Beta -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: comby -Requires-Dist: python-levenshtein -Requires-Dist: tabulate -Dynamic: classifier -Dynamic: description -Dynamic: description-content-type -Dynamic: home-page -Dynamic: keywords -Dynamic: license -Dynamic: license-file -Dynamic: requires-dist -Dynamic: summary - -This is a tool based on source-based rewrite of code lines for mutation generation, including -multi-language rules aided by special rules for languages or even projects. Originally, the approach used only regular expressions, -treating code as text. However, there is also a mode that can use the [Comby](https://github.com/comby-tools/comby) tool -for more sophisticated mutation that produces fewer invalid mutants. Regular-expression based mutation works well, in our experience; -comby-aided mutation works even better. The key advantage of either approach is that the tool can probably mutate approximately *any* interesting source code you have, and language changes don't force -rewriting of the mutation tool. To use the comby mode, just make sure comby is installed and add `--comby` when you run `mutate`. - -More information on this project can be found in a [2024 FSE paper](https://agroce.github.io/fse24.pdf), and in the original [2018 ICSE Tool Paper](https://agroce.github.io/icse18t.pdf). - -A [guest blog post](https://blog.trailofbits.com/2019/01/23/fuzzing-an-api-with-deepstate-part-2/) for Trail of Bits shows how to use the universalmutator to help improve a C/C++ API fuzzing effort using [DeepState](https://github.com/trailofbits/deepstate) and libFuzzer. - -The universalmutator has support for extracting coverage information to guide mutation from the [TSTL](https://github.com/agroce/tstl) testing tool for Python. - -HOW TO USE IT -============= - -To use this, you should really just do: - -`pip install universalmutator` - -then - -`mutate --help` - -SIMPLE EXAMPLE USAGE -==================== - -`mutate foo.py` - -or - -`mutate foo.swift` - -should, if you have the appropriate compilers installed, generate a bunch of valid, non-trivially redundant, mutants. - - -A MORE COMPLEX EXAMPLE -====================== - -Sometimes the mutated code needs to be built with a more complicated command than a simple compiler call, and of course you want help discovering which mutants are killed and not killed. For example, to mutate and test mutants for the mandelbrot plotting example included in the PROGRAMMING RUST book (http://shop.oreilly.com/product/0636920040385.do), just do this: - - - git clone https://github.com/ProgrammingRust/mandelbrot - cd mandelbrot - cargo build - target/debug/mandelbrot origmandel.png 1000x750 -1.20,0.35 -1,0.20 - mkdir mutants - mutate src/main.rs --mutantDir mutants --noCheck - analyze_mutants src/main.rs "cargo clean; cargo build; rm mandel.png; target/debug/mandelbrot mandel.png 1000x750 -1.20,0.35 -1,0.20; diff mandel.png origmandel.png" --mutantDir mutants - -(It will go faster if you edit `main.rs` to lower the maximum number of threads used to something like 8, not 90.) At the moment, this won't use any Trivial Compiler Equivalence, but still kills about 60% of the 1000+ mutants. The killed mutant filenames will be in `killed.txt` and the non-killed ones in `not-killed.txt`. - -Working with something like maven is very similar, except you can probably edit the complicated build/clean stuff to just a 'mvn test' or similar. - -CURRENTLY SUPPORTED LANGUAGES -============================= - -The tool will likely mutate other things, if you tell it they are "c" or something, but there is auto-detection based on file ending and specific rule support for: - -``` -C -C++ -Java -JavaScript -Python -Swift -R -Rust -Go -Lisp -Fortran -Solidity -Vyper -Fe -``` - -(the last three are smart contract languages for the Ethereum blockchain). - -All but C, C++, JavaScript, and Go will try, by default, to compile the mutated -file and use TCE to detect redundancy. Of course, build dependencies -may frustrate this process, in which case --noCheck will turn off TCE -and just dump all the mutants in the directory, for pruning using a -real build process. In the long run, we plan to integrate with -standard build systems to avoid this problem, and with automated test -generation systems such as TSTL (https://github.com/agroce/tstl) for -Python or Echidna for Solidity -(https://github.com/trailofbits/echidna). Even now, however, with -`analyze_mutants` it is fairly easy to set up automatic evaluation of -your automated test generator. - -MUTATING SOLIDITY CODE -====================== - -The universalmutator has been most frequently applied to smart -contracts written in the Solidity language. It supports a few special -features that are particularly useful in this context. - -First, -Solidity libraries are often written with only `internal` functions ---- and the compiler will not emit code for such functions if you -compile a library by itself, resulting in no non-redundant mutants. -In order to handle this case, `mutate` can take a `--compile` option -that specifies another file (a contract using the library, or the -tests in question) that is used to check whether mutants are -redundant. - -Second, swapping adjacent lines of code is a seldom-used mutation -operator that is unusually attractive in a Solidity context because -swapping a state-changing operation and a requirement may reveal that -testing is incapable of detecting some -[re-entrancy](https://github.com/crytic/not-so-smart-contracts/tree/master/reentrancy) -vulnerabilities. The testing may notice the absence of the check, but -not a mis-ordering, and these mutants may reveal that. To add code -swaps to your mutations, just add `--swap` to the `mutate` call. Note -that swaps work in any language; they are just particularly appealing -for smart contracts. - -MORE INFORMATON -=============== - -For much more information, again see https://agroce.github.io/icse18t.pdf -- demo/tool paper at ICSE 18 and especially our full FSE 2024 paper -- https://agroce.github.io/fse24.pdf -- the latter discusses the latest version of the tool/approach, and includes a comparison with many other mutation testing tools. - -The aim of this project is partly to see how quickly mutation can be applied to new languages, partly how much the work of a tool can be -offloaded to the compiler / test analysis tools. - -Authors: Alex Groce, Josie Holmes, Darko Marinov, August Shi, Lingming Zhang, Kush Jain, Rijnard van Tonder, Sourav Deb diff --git a/universalmutator.egg-info/SOURCES.txt b/universalmutator.egg-info/SOURCES.txt deleted file mode 100644 index e8ee0fb..0000000 --- a/universalmutator.egg-info/SOURCES.txt +++ /dev/null @@ -1,68 +0,0 @@ -LICENSE -README.md -setup.py -tests/test_comments_rules.py -tests/test_foo_example.py -universalmutator/__init__.py -universalmutator/analyze.py -universalmutator/c_handler.py -universalmutator/checkcov.py -universalmutator/cpp_handler.py -universalmutator/fe_handler.py -universalmutator/findmissing.py -universalmutator/fortran_handler.py -universalmutator/genmutants.py -universalmutator/go_handler.py -universalmutator/intersect.py -universalmutator/java_handler.py -universalmutator/javascript_handler.py -universalmutator/lisp_handler.py -universalmutator/mutator.py -universalmutator/prioritize.py -universalmutator/prune.py -universalmutator/python_handler.py -universalmutator/r_handler.py -universalmutator/rust_handler.py -universalmutator/show.py -universalmutator/solidity_handler.py -universalmutator/swift_handler.py -universalmutator/utils.py -universalmutator/vyper_handler.py -universalmutator.egg-info/PKG-INFO -universalmutator.egg-info/SOURCES.txt -universalmutator.egg-info/dependency_links.txt -universalmutator.egg-info/entry_points.txt -universalmutator.egg-info/requires.txt -universalmutator.egg-info/top_level.txt -universalmutator/comby/c.rules -universalmutator/comby/c_like.rules -universalmutator/comby/cpp.rules -universalmutator/comby/fortran.rules -universalmutator/comby/go.rules -universalmutator/comby/java.rules -universalmutator/comby/lisp.rules -universalmutator/comby/none.rules -universalmutator/comby/python.rules -universalmutator/comby/r.rules -universalmutator/comby/rust.rules -universalmutator/comby/solidity.rules -universalmutator/comby/swift.rules -universalmutator/comby/universal.rules -universalmutator/comby/vyper.rules -universalmutator/static/c.rules -universalmutator/static/c_like.rules -universalmutator/static/cpp.rules -universalmutator/static/fe.rules -universalmutator/static/fortran.rules -universalmutator/static/go.rules -universalmutator/static/java.rules -universalmutator/static/javascript.rules -universalmutator/static/lisp.rules -universalmutator/static/none.rules -universalmutator/static/python.rules -universalmutator/static/r.rules -universalmutator/static/rust.rules -universalmutator/static/solidity.rules -universalmutator/static/swift.rules -universalmutator/static/universal.rules -universalmutator/static/vyper.rules \ No newline at end of file diff --git a/universalmutator.egg-info/dependency_links.txt b/universalmutator.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/universalmutator.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/universalmutator.egg-info/entry_points.txt b/universalmutator.egg-info/entry_points.txt deleted file mode 100644 index e75b6a1..0000000 --- a/universalmutator.egg-info/entry_points.txt +++ /dev/null @@ -1,8 +0,0 @@ -[console_scripts] -analyze_mutants = universalmutator.analyze:main -check_covered = universalmutator.checkcov:main -intersect_mutants = universalmutator.intersect:main -mutate = universalmutator.genmutants:main -prioritize_mutants = universalmutator.prioritize:main -prune_mutants = universalmutator.prune:main -show_mutants = universalmutator.show:main diff --git a/universalmutator.egg-info/requires.txt b/universalmutator.egg-info/requires.txt deleted file mode 100644 index f68a6dc..0000000 --- a/universalmutator.egg-info/requires.txt +++ /dev/null @@ -1,3 +0,0 @@ -comby -python-levenshtein -tabulate diff --git a/universalmutator.egg-info/top_level.txt b/universalmutator.egg-info/top_level.txt deleted file mode 100644 index 132847f..0000000 --- a/universalmutator.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -universalmutator From 4adf5968b9c0e071081165c66bc91c83434950df Mon Sep 17 00:00:00 2001 From: jag2289-blip Date: Sat, 25 Apr 2026 02:35:42 +0000 Subject: [PATCH 18/18] Deleted build folder not needed --- build/lib/universalmutator/__init__.py | 0 build/lib/universalmutator/analyze.py | 335 ---------- build/lib/universalmutator/c_handler.py | 4 - build/lib/universalmutator/checkcov.py | 77 --- build/lib/universalmutator/comby/c.rules | 0 build/lib/universalmutator/comby/c_like.rules | 20 - build/lib/universalmutator/comby/cpp.rules | 14 - .../lib/universalmutator/comby/fortran.rules | 112 ---- build/lib/universalmutator/comby/go.rules | 6 - build/lib/universalmutator/comby/java.rules | 10 - build/lib/universalmutator/comby/lisp.rules | 8 - build/lib/universalmutator/comby/none.rules | 0 build/lib/universalmutator/comby/python.rules | 47 -- build/lib/universalmutator/comby/r.rules | 1 - build/lib/universalmutator/comby/rust.rules | 2 - .../lib/universalmutator/comby/solidity.rules | 44 -- build/lib/universalmutator/comby/swift.rules | 13 - .../universalmutator/comby/universal.rules | 96 --- build/lib/universalmutator/comby/vyper.rules | 0 build/lib/universalmutator/cpp_handler.py | 4 - build/lib/universalmutator/fe_handler.py | 45 -- build/lib/universalmutator/findmissing.py | 54 -- build/lib/universalmutator/fortran_handler.py | 4 - build/lib/universalmutator/genmutants.py | 623 ------------------ build/lib/universalmutator/go_handler.py | 4 - build/lib/universalmutator/intersect.py | 33 - build/lib/universalmutator/java_handler.py | 26 - .../universalmutator/javascript_handler.py | 4 - build/lib/universalmutator/lisp_handler.py | 4 - build/lib/universalmutator/mutator.py | 325 --------- build/lib/universalmutator/prioritize.py | 131 ---- build/lib/universalmutator/prune.py | 136 ---- build/lib/universalmutator/python_handler.py | 85 --- build/lib/universalmutator/r_handler.py | 4 - build/lib/universalmutator/rust_handler.py | 28 - build/lib/universalmutator/show.py | 71 -- .../lib/universalmutator/solidity_handler.py | 59 -- build/lib/universalmutator/static/c.rules | 0 .../lib/universalmutator/static/c_like.rules | 20 - build/lib/universalmutator/static/cpp.rules | 14 - build/lib/universalmutator/static/fe.rules | 0 .../lib/universalmutator/static/fortran.rules | 112 ---- build/lib/universalmutator/static/go.rules | 6 - build/lib/universalmutator/static/java.rules | 12 - .../universalmutator/static/javascript.rules | 6 - build/lib/universalmutator/static/lisp.rules | 7 - build/lib/universalmutator/static/none.rules | 0 .../lib/universalmutator/static/python.rules | 43 -- build/lib/universalmutator/static/r.rules | 1 - build/lib/universalmutator/static/rust.rules | 2 - .../universalmutator/static/solidity.rules | 50 -- build/lib/universalmutator/static/swift.rules | 15 - .../universalmutator/static/universal.rules | 96 --- build/lib/universalmutator/static/vyper.rules | 0 build/lib/universalmutator/swift_handler.py | 29 - build/lib/universalmutator/utils.py | 221 ------- build/lib/universalmutator/vyper_handler.py | 31 - 57 files changed, 3094 deletions(-) delete mode 100644 build/lib/universalmutator/__init__.py delete mode 100644 build/lib/universalmutator/analyze.py delete mode 100644 build/lib/universalmutator/c_handler.py delete mode 100644 build/lib/universalmutator/checkcov.py delete mode 100644 build/lib/universalmutator/comby/c.rules delete mode 100644 build/lib/universalmutator/comby/c_like.rules delete mode 100644 build/lib/universalmutator/comby/cpp.rules delete mode 100644 build/lib/universalmutator/comby/fortran.rules delete mode 100644 build/lib/universalmutator/comby/go.rules delete mode 100644 build/lib/universalmutator/comby/java.rules delete mode 100644 build/lib/universalmutator/comby/lisp.rules delete mode 100644 build/lib/universalmutator/comby/none.rules delete mode 100644 build/lib/universalmutator/comby/python.rules delete mode 100644 build/lib/universalmutator/comby/r.rules delete mode 100644 build/lib/universalmutator/comby/rust.rules delete mode 100644 build/lib/universalmutator/comby/solidity.rules delete mode 100644 build/lib/universalmutator/comby/swift.rules delete mode 100644 build/lib/universalmutator/comby/universal.rules delete mode 100644 build/lib/universalmutator/comby/vyper.rules delete mode 100644 build/lib/universalmutator/cpp_handler.py delete mode 100644 build/lib/universalmutator/fe_handler.py delete mode 100644 build/lib/universalmutator/findmissing.py delete mode 100644 build/lib/universalmutator/fortran_handler.py delete mode 100644 build/lib/universalmutator/genmutants.py delete mode 100644 build/lib/universalmutator/go_handler.py delete mode 100644 build/lib/universalmutator/intersect.py delete mode 100644 build/lib/universalmutator/java_handler.py delete mode 100644 build/lib/universalmutator/javascript_handler.py delete mode 100644 build/lib/universalmutator/lisp_handler.py delete mode 100644 build/lib/universalmutator/mutator.py delete mode 100644 build/lib/universalmutator/prioritize.py delete mode 100644 build/lib/universalmutator/prune.py delete mode 100644 build/lib/universalmutator/python_handler.py delete mode 100644 build/lib/universalmutator/r_handler.py delete mode 100644 build/lib/universalmutator/rust_handler.py delete mode 100644 build/lib/universalmutator/show.py delete mode 100644 build/lib/universalmutator/solidity_handler.py delete mode 100644 build/lib/universalmutator/static/c.rules delete mode 100644 build/lib/universalmutator/static/c_like.rules delete mode 100644 build/lib/universalmutator/static/cpp.rules delete mode 100644 build/lib/universalmutator/static/fe.rules delete mode 100644 build/lib/universalmutator/static/fortran.rules delete mode 100644 build/lib/universalmutator/static/go.rules delete mode 100644 build/lib/universalmutator/static/java.rules delete mode 100644 build/lib/universalmutator/static/javascript.rules delete mode 100644 build/lib/universalmutator/static/lisp.rules delete mode 100644 build/lib/universalmutator/static/none.rules delete mode 100644 build/lib/universalmutator/static/python.rules delete mode 100644 build/lib/universalmutator/static/r.rules delete mode 100644 build/lib/universalmutator/static/rust.rules delete mode 100644 build/lib/universalmutator/static/solidity.rules delete mode 100644 build/lib/universalmutator/static/swift.rules delete mode 100644 build/lib/universalmutator/static/universal.rules delete mode 100644 build/lib/universalmutator/static/vyper.rules delete mode 100644 build/lib/universalmutator/swift_handler.py delete mode 100644 build/lib/universalmutator/utils.py delete mode 100644 build/lib/universalmutator/vyper_handler.py diff --git a/build/lib/universalmutator/__init__.py b/build/lib/universalmutator/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/universalmutator/analyze.py b/build/lib/universalmutator/analyze.py deleted file mode 100644 index d48450b..0000000 --- a/build/lib/universalmutator/analyze.py +++ /dev/null @@ -1,335 +0,0 @@ -from __future__ import print_function - -import difflib -import subprocess -import sys -import platform -import glob -import shutil -import signal -import time -import random -import os -import py_compile - -def main(): - - isWindows = platform.system() - args = sys.argv - - if ("--help" in args) or (len(sys.argv) < 3): - if len(sys.argv) < 3: - print("ERROR: analyze_mutants requires at least two arguments\n") - print("USAGE: analyze_mutants [--mutantDir ] [--fromFile ]") - print(" is command to execute to run tests; non-zero return indicates mutant killed") - print(" --mutantDir: directory with all mutants; defaults to current directory") - print(" --fromFile: file containing list of mutants to process; others ignored") - print(" --timeout : change the timeout setting") - print(" --show: show mutants") - print(" --verbose: show mutants and output of analysis") - print(" --seed: random seed for shuffling of mutants") - print(" --noShuffle: do not randomize order of mutants") - print(" --resume: use existing killed.txt and notkilled.txt, resume mutation analysis") - print(" --prefix: add a prefix to killed.txt and notkilled.txt") - print(" --numMutants: run with specific number of mutants") - print(" --compileCommand: compile command to run in selecting mutants") - sys.exit(0) - - verbose = "--verbose" in sys.argv - if verbose: - args.remove("--verbose") - - showM = "--show" in sys.argv - if showM: - args.remove("--show") - - resume = "--resume" in sys.argv - if resume: - args.remove("--resume") - - noShuffle = "--noShuffle" in sys.argv - if noShuffle: - args.remove("--noShuffle") - - prefix = None - try: - prefixpos = args.index("--prefix") - except ValueError: - prefixpos = -1 - - if prefixpos != -1: - prefix = args[prefixpos + 1] - args.remove("--prefix") - args.remove(prefix) - - fromFile = None - try: - filepos = args.index("--fromFile") - except ValueError: - filepos = -1 - - if filepos != -1: - fromFile = args[filepos + 1] - args.remove("--fromFile") - args.remove(fromFile) - - seed = None - try: - seedpos = args.index("--seed") - except ValueError: - seedpos = -1 - - if seedpos != -1: - seed = args[seedpos + 1] - args.remove("--seed") - args.remove(seed) - seed = int(seed) - - timeout = 30 - try: - topos = args.index("--timeout") - except ValueError: - topos = -1 - - if topos != -1: - timeout = args[topos + 1] - args.remove("--timeout") - args.remove(timeout) - timeout = float(timeout) - - numMutants = -1 - try: - nmpos = args.index("--numMutants") - except ValueError: - nmpos = -1 - - if nmpos != -1: - numMutants = args[nmpos + 1] - args.remove("--numMutants") - args.remove(numMutants) - numMutants = int(numMutants) - - compileCommand = None - try: - ccmdpos = args.index("--compileCommand") - except ValueError: - ccmdpos = -1 - - if ccmdpos != -1: - compileCommand = args[ccmdpos + 1] - args.remove("--compileCommand") - args.remove(compileCommand) - - onlyMutants = None - if fromFile is not None: - with open(fromFile, 'r') as file: - onlyMutants = file.read().split() - - mdir = "." - try: - mdirpos = args.index("--mutantDir") - except ValueError: - mdirpos = -1 - - if mdirpos != -1: - mdir = args[mdirpos + 1] - args.remove("--mutantDir") - args.remove(mdir) - if mdir[-1] != "/": - mdir += "/" - - src = args[1] - tstCmd = [args[2]] - ignore = [] - if len(args) > 3: - with open(sys.argv[3]) as file: - for l in file: - ignore.append(l.split()[0]) - - srcBase = src.split("/")[-1] - srcEnd = "." + ((src.split(".")[-1]).split("/")[-1]) - - count = 0.0 - killCount = 0.0 - - killFileName = "killed.txt" - notkillFileName = "notkilled.txt" - if prefix is not None: - killFileName = prefix + "." + killFileName - notkillFileName = prefix + "." + notkillFileName - - print("ANALYZING", src) - print("COMMAND: **", tstCmd, "**") - - allTheMutants = glob.glob(mdir + srcBase.replace(srcEnd, ".mutant*" + srcEnd)) - - if onlyMutants is not None: - newMutants = [] - for f1 in onlyMutants: - for f2 in allTheMutants: - if f2.split("/")[-1] == f1: - newMutants.append(f2) - allTheMutants = newMutants - - if seed is not None: - random.seed(seed) - - if not noShuffle: - random.shuffle(allTheMutants) - - allStart = time.time() - - if resume: - alreadyKilled = [] - alreadyNotKilled = [] - if not (os.path.exists(killFileName) and os.path.exists(notkillFileName)): - print("ATTEMPTING TO RESUME, BUT NO PREVIOUS RESULTS FOUND") - else: - with open(killFileName, 'r') as killed: - with open(notkillFileName, 'r') as notkilled: - for line in killed: - if line == "\n": - continue - alreadyKilled.append(line[:-1]) - count += 1 - killCount += 1 - for line in notkilled: - if line == "\n": - continue - alreadyNotKilled.append(line[:-1]) - count += 1 - print("RESUMING FROM EXISTING RUN, WITH", int(killCount), "KILLED MUTANTS OUT OF", int(count)) - - # numMutants = -1 implies no --numMutants argument was provided - totalMutants = min(numMutants, len(allTheMutants)) if numMutants > 0 else len(allTheMutants) - - with open(os.devnull, 'w') as dnull: - with open(killFileName, 'w') as killed: - with open(notkillFileName, 'w') as notkilled: - if resume: - for line in alreadyKilled: - killed.write(line + "\n") - killed.flush() - for line in alreadyNotKilled: - notkilled.write(line + "\n") - notkilled.flush() - for f in allTheMutants: - if resume: - if (f.split("/")[-1] in alreadyKilled) or (f.split("/")[-1] in alreadyNotKilled): - continue - if f in ignore: - print(f, "SKIPPED") - if numMutants != -1 and compileCommand is not None: - if runCmd(compileCommand, src, f) != "VALID": - continue - - print("=" * 80) - print("#" + str(int(count) + 1) + ":", end=" ") - print("[" + str(round(time.time() - allStart, 2)) + "s", end=" ") - print(str(round(count / totalMutants * 100.0, 2)) + "% DONE]") - if verbose or showM: - print("MUTANT:", f) - with open(src, 'r') as ff: - fromLines = ff.readlines() - with open(f, 'r') as tf: - toLines = tf.readlines() - diff = difflib.context_diff(fromLines, toLines, "Original", "Mutant") - print(''.join(diff)) - print() - sys.stdout.flush() - print("RUNNING", f + "...") - sys.stdout.flush() - try: - shutil.copy(src, src + ".um.backup") - shutil.copy(f, src) - if srcEnd == ".py": - py_compile.compile(src) - - if isWindows: - ctstCmd = ['set "CURRENT_MUTANT_SOURCE=' + f + '" && ' + tstCmd[0]] - else: - ctstCmd = ['export CURRENT_MUTANT_SOURCE="' + f + '"; ' + tstCmd[0]] - start = time.time() - - if not verbose: - if isWindows: - P = subprocess.Popen(ctstCmd, shell=True, stderr=dnull, stdout=dnull, - start_new_session=True) - else: - P = subprocess.Popen(ctstCmd, shell=True, stderr=dnull, stdout=dnull, - preexec_fn=os.setsid) - else: - if isWindows: - P = subprocess.Popen(ctstCmd, shell=True, start_new_session=True) - else: - P = subprocess.Popen(ctstCmd, shell=True, preexec_fn=os.setsid) - - try: - while P.poll() is None and (time.time() - start) < timeout: - time.sleep(0.05) - finally: - if P.poll() is None: - print() - print("HAD TO TERMINATE ANALYSIS (TIMEOUT OR EXCEPTION)") - - if isWindows: - os.kill(P.pid, signal.SIGTERM) - else: - os.killpg(os.getpgid(P.pid), signal.SIGTERM) - - # Avoid any weird race conditions from grabbing the return code - time.sleep(0.05) - r = P.returncode - - runtime = time.time() - start - - count += 1 - if numMutants != -1 and count >= numMutants: - break - if r == 0: - print(f, "NOT KILLED") - notkilled.write(f.split("/")[-1] + "\n") - notkilled.flush() - else: - killCount += 1 - print(f, "KILLED IN", runtime, "(RETURN CODE", str(r) + ")") - killed.write(f.split("/")[-1] + "\n") - killed.flush() - print(" RUNNING SCORE:", killCount / count) - sys.stdout.flush() - finally: - shutil.copy(src + ".um.backup", src) - os.remove(src + ".um.backup") - if os.path.exists(".um.mutant_output." + str(os.getpid())): - os.remove(".um.mutant_output." + str(os.getpid())) - - print("=" * 80) - - if count == 0: - print("!!! No valid mutants found! Make sure you specified the right mutant directory !!!") - return - print("MUTATION SCORE:", killCount / count) - - -def runCmd(cmd, sourceFile, mutantFile): - if "MUTANT" not in cmd: - # We asssume if the MUTANT isn't part of the command, - # we need to move it into place, before, e.g., make - backupName = sourceFile + ".um.backup." + str(os.getpid()) - shutil.copy(sourceFile, backupName) - shutil.copy(mutantFile, sourceFile) - try: - with open(".um.mutant_output." + str(os.getpid()), 'w') as file: - r = subprocess.call([cmd.replace("MUTANT", mutantFile)], - shell=True, stderr=file, stdout=file) - if r == 0: - return "VALID" - return "INVALID" - finally: - # If we moved the mutant in, restore original - if "MUTANT" not in cmd: - shutil.copy(backupName, sourceFile) - - -if __name__ == '__main__': - main() diff --git a/build/lib/universalmutator/c_handler.py b/build/lib/universalmutator/c_handler.py deleted file mode 100644 index 2222de5..0000000 --- a/build/lib/universalmutator/c_handler.py +++ /dev/null @@ -1,4 +0,0 @@ -dumb = True - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - return "VALID" diff --git a/build/lib/universalmutator/checkcov.py b/build/lib/universalmutator/checkcov.py deleted file mode 100644 index adbf48e..0000000 --- a/build/lib/universalmutator/checkcov.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import print_function - -import sys -import glob - - -def main(): - - args = sys.argv - - if ("--help" in args) or (len(sys.argv) < 4): - if len(sys.argv) < 4: - print("ERROR: check_covered requires at least three arguments\n") - print("USAGE: check_covered [--tstl] [--mutantDir directory]") - print(" --mutantDir: directory to put generated mutants in; defaults to current directory") - print(" --tstl: process that is output from TSTL internal report") - sys.exit(0) - - mdir = "." - try: - mdirpos = args.index("--mutantDir") - except ValueError: - mdirpos = -1 - - if mdirpos != -1: - mdir = args[mdirpos + 1] - args.remove("--mutantDir") - args.remove(mdir) - if mdir[-1] != "/": - mdir += "/" - - src = args[1] - coverFile = args[2] - outFile = args[3] - - tstl = "--tstl" in sys.argv - - srcBase = src.split("/")[-1] - srcEnd = src.split(".")[-1] - - with open(coverFile) as file: - if not tstl: - lines = list(map(int, file.read().split())) - else: - lines = [] - for l in file: - if "LINES" in l: - if src not in l: - continue - db = l.split("[")[1] - d = db[:-2].split(",") - for line in d: - lines.append(int(line)) - - with open(outFile, 'w') as coveredFile: - for f in glob.glob( - mdir + - srcBase.replace( - srcEnd, - "mutant.*." + - srcEnd)): - with open(src, 'r') as sf: - sfLines = sf.readlines() - with open(f, 'r') as mf: - mfLines = mf.readlines() - line = 1 - for i in range(0, min(len(sfLines), len(mfLines))): - if sfLines[i] != mfLines[i]: - break - line += 1 - print(f, line) - if line in lines: - coveredFile.write(f.split("/")[-1] + "\n") - - -if __name__ == '__main__': - main() diff --git a/build/lib/universalmutator/comby/c.rules b/build/lib/universalmutator/comby/c.rules deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/universalmutator/comby/c_like.rules b/build/lib/universalmutator/comby/c_like.rules deleted file mode 100644 index 0b114c7..0000000 --- a/build/lib/universalmutator/comby/c_like.rules +++ /dev/null @@ -1,20 +0,0 @@ -#include ==> DO_NOT_MUTATE - -:[[expr]]:[rest]:[lf~[\n]] ==> /*:[expr]:[rest]*/:[lf] -if (:[cond]) ==> if (!(:[cond])) -if(:[cond]) ==> if ((!:[cond])) -if (:[cond]) ==> if (0==1) -if(:[cond]) ==> if(0==1) -if (:[cond]) ==> if (1==1) -if(:[cond]) ==> if(1==1) -while (:[cond]) ==> while (!(:[cond])) -while(:[cond]) ==> while(!:([cond])) -else ==> - -||:[expr] ==> || (0==1) -:[expr]|| ==> (0==1) || -&&:[expr] ==> && (1==1) -:[expr]&& ==> (1==1) && - -// ==> SKIP_MUTATING_REST - diff --git a/build/lib/universalmutator/comby/cpp.rules b/build/lib/universalmutator/comby/cpp.rules deleted file mode 100644 index b164656..0000000 --- a/build/lib/universalmutator/comby/cpp.rules +++ /dev/null @@ -1,14 +0,0 @@ -any_of ==> all_of -any_of ==> none_of -all_of ==> none_of -all_of ==> any_of -none_of ==> any_of -none_of ==> all_of -at(:[hole]) ==> at(0) -at (:[hole]) ==> at (0) -front_inserter ==> back_inserter -back_inserter ==> front_inserter -min_element ==> max_element -max_element ==> min_element -accumulate ==> inner_product -inner_product ==> accumulate diff --git a/build/lib/universalmutator/comby/fortran.rules b/build/lib/universalmutator/comby/fortran.rules deleted file mode 100644 index d5eba99..0000000 --- a/build/lib/universalmutator/comby/fortran.rules +++ /dev/null @@ -1,112 +0,0 @@ -\*\* ==> + -\*\* ==> - -\*\* ==> / - -\+ ==> ** -- ==> ** -\* ==> ** -([^\*/])/([^\*/]) ==> \1**\2 - -\.lt\. ==> .eq. -\.lt\. ==> .ne. -\.lt\. ==> .le. -\.lt\. ==> .gt. -\.lt\. ==> .ge. - -\.le\. ==> .eq. -\.le\. ==> .ne. -\.le\. ==> .lt. -\.le\. ==> .gt. -\.le\. ==> .ge. - -\.gt\. ==> .eq. -\.gt\. ==> .ne. -\.gt\. ==> .le. -\.gt\. ==> .lt. -\.gt\. ==> .ge. - -\.ge\. ==> .eq. -\.ge\. ==> .ne. -\.ge\. ==> .le. -\.ge\. ==> .gt. -\.ge\. ==> .lt. - -\.eq\. ==> .ge. -\.ge\. ==> .ne. -\.ge\. ==> .le. -\.ge\. ==> .gt. -\.ge\. ==> .lt. - -\.ne\. ==> .ge. -\.ne\. ==> .eq. -\.ne\. ==> .le. -\.ne\. ==> .gt. -\.ne\. ==> .lt. - -\.and\. ==> .or. -\.and\. ==> .eqv. -\.and\. ==> .neqv. - -\.or\. ==> .and. -\.or\. ==> .eqv. -\.or\. ==> .neqv. - -\.eqv\. ==> .or. -\.eqv\. ==> .and. -\.eqv\. ==> .neqv. - -\.neqv\. ==> .or. -\.neqv\. ==> .and. -\.neqv\. ==> .eqv - -\.LT\. ==> .EQ. -\.LT\. ==> .NE. -\.LT\. ==> .LE. -\.LT\. ==> .GT. -\.LT\. ==> .GE. - -\.LE\. ==> .EQ. -\.LE\. ==> .NE. -\.LE\. ==> .LT. -\.LE\. ==> .GT. -\.LE\. ==> .GE. - -\.GT\. ==> .EQ. -\.GT\. ==> .NE. -\.GT\. ==> .LE. -\.GT\. ==> .LT. -\.GT\. ==> .GE. - -\.GE\. ==> .EQ. -\.GE\. ==> .NE. -\.GE\. ==> .LE. -\.GE\. ==> .GT. -\.GE\. ==> .LT. - -\.EQ\. ==> .GE. -\.GE\. ==> .NE. -\.GE\. ==> .LE. -\.GE\. ==> .GT. -\.GE\. ==> .LT. - -\.NE\. ==> .GE. -\.NE\. ==> .EQ. -\.NE\. ==> .LE. -\.NE\. ==> .GT. -\.NE\. ==> .LT. - -\.AND\. ==> .OR. -\.AND\. ==> .EQV. -\.AND\. ==> .NEQV. - -\.OR\. ==> .AND. -\.OR\. ==> .EQV. -\.OR\. ==> .NEQV. - -\.EQV\. ==> .OR. -\.EQV\. ==> .AND. -\.EQV\. ==> .NEQV. - -\.NEQV\. ==> .OR. -\.NEQV\. ==> .AND. -\.NEQV\. ==> .EQV \ No newline at end of file diff --git a/build/lib/universalmutator/comby/go.rules b/build/lib/universalmutator/comby/go.rules deleted file mode 100644 index eca3edb..0000000 --- a/build/lib/universalmutator/comby/go.rules +++ /dev/null @@ -1,6 +0,0 @@ -:[[a]] && :[[b]] ==> :[[a]] && true -:[[a]] && :[[b]] ==> true && :[[b]] -:[[a]] || :[[b]] ==> :[[a]] || false -:[[a]] || :[[b]] ==> :[[a]] || false - -defer ==> \ No newline at end of file diff --git a/build/lib/universalmutator/comby/java.rules b/build/lib/universalmutator/comby/java.rules deleted file mode 100644 index d56c50a..0000000 --- a/build/lib/universalmutator/comby/java.rules +++ /dev/null @@ -1,10 +0,0 @@ -:[s~[ \t]*]import ==> DO_NOT_MUTATE -:[s~[ \t]*]@:[annotation] ==> DO_NOT_MUTATE - - -synchronized ==> - -:[a]&&:[b] ==> :[a] && true -:[a]&&:[b] ==> true && :[b] -:[a]||:[b] ==> :[a] || false -:[a]||:[b] ==> false || :[b] diff --git a/build/lib/universalmutator/comby/lisp.rules b/build/lib/universalmutator/comby/lisp.rules deleted file mode 100644 index 62dba25..0000000 --- a/build/lib/universalmutator/comby/lisp.rules +++ /dev/null @@ -1,8 +0,0 @@ -and ==> or -or ==> and -not :cond ==> :cond -:cond ==> not :cond -or ==> or T -and ==> and nil -T ==> nil -nil ==> T diff --git a/build/lib/universalmutator/comby/none.rules b/build/lib/universalmutator/comby/none.rules deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/universalmutator/comby/python.rules b/build/lib/universalmutator/comby/python.rules deleted file mode 100644 index 807ea2d..0000000 --- a/build/lib/universalmutator/comby/python.rules +++ /dev/null @@ -1,47 +0,0 @@ -:[s~[ \t]*]import ==> DO_NOT_MUTATE - -if :[cond] ==> if not (:[cond]) -while :[cond] ==> while not (:[cond]) - -:[body]continue ==> :[body] break -:[body]break ==> :[body] continue - -print(:[1], :[2], :[3]) ==> print(:[3], :[2], :[1]) -:[[v]] = :[[fn]](:[x]) ==> :[[v]] = :[[fn]]((:[x] - 1)) - -:[[a]] and :[[b]] ==> :[[a]] or :[[b]] -:[[a]] or :[[b]] ==> :[[a]] and :[[b]] -:[[a]] and :[[b]] ==> :[[a]] or True -:[[a]] or :[[b]] ==> :[[a]] or False -:[[a]] and :[[b]] ==> True and :[[b]] -:[[a]] or :[[b]] ==> False or :[[b]] - -not :[[a]] ==> :[[a]] - -return :[expr]:[lf~[\n]] ==> return None:[lf] -:[ s]:[expr]:[lf~[\n]] ==> :[s]pass:[lf] - -//:[[a]] ==> /:[[a]] -/:[[a]] ==> //:[[a]] - -True ==> False - -[:[expr]] ==> [] -[:[first],:[rest]] ==> [:[first]] -[:[first],:[rest]] ==> [:[rest]] -,:[s~[ \t]*]:[[x]]:[bra~[\]]] ==> ] - -{:[expr]} ==> {} -{:[first],:[rest]} ==> {:[first]} -{:[first],:[rest]} ==> {:[rest]} -,:[s~[ \t]*]:[[x]]:[bra~[\}]] ==> } - - -,:[s~[ \t]*]:[[item1]],:[s~[ \t]*]:[[item2]] ==> ,:[[item2]],:[[item1]] -:[par~[(]]:[~[ \t]*]:[[item1]],:[~[ \t]*]:[[item2]] ==> :[par]:[[item2]],:[[item1]] -:[bra~[\[]]:[~[ \t]*]:[[item1]],:[~[ \t]*]:[[item2]] ==> :[bra]:[[item2]],:[[item1]] - -,:[item], ==> , -':[str]' ==> '' - -@:[s~[ \t]*]:[annotation] :[body] ==> :[body] \ No newline at end of file diff --git a/build/lib/universalmutator/comby/r.rules b/build/lib/universalmutator/comby/r.rules deleted file mode 100644 index 1c69a9e..0000000 --- a/build/lib/universalmutator/comby/r.rules +++ /dev/null @@ -1 +0,0 @@ -# ==> SKIP_MUTATING_REST \ No newline at end of file diff --git a/build/lib/universalmutator/comby/rust.rules b/build/lib/universalmutator/comby/rust.rules deleted file mode 100644 index 26b8bd2..0000000 --- a/build/lib/universalmutator/comby/rust.rules +++ /dev/null @@ -1,2 +0,0 @@ -true ==> false -false ==> true diff --git a/build/lib/universalmutator/comby/solidity.rules b/build/lib/universalmutator/comby/solidity.rules deleted file mode 100644 index 05e69c7..0000000 --- a/build/lib/universalmutator/comby/solidity.rules +++ /dev/null @@ -1,44 +0,0 @@ -:[prefix~[^u]]fixed ==> :[prefix]ufixed -ufixed ==> fixed -int16 ==> int8 -int32 ==> int16 -int64 ==> int32 -memory ==> storage -storage ==> memory -view ==> pure -pure ==> view -constant ==> pure -payable ==> -private ==> -msg.sender ==> tx.origin -tx.origin ==> msg.sender -ether ==> wei -:[prefix~wei|finney|szabo] ==> ether -:[prefix~ether|finney|szabo] ==> wei -:[prefix~wei|ether|szao] ==> finney -:[prefix~wei|finney|ether] ==> szabo -:[prefix~minutes|days|hours|weeks|years] ==> seconds -:[prefix~seconds|days|hours|weeks|years] ==> minutes -:[prefix~seconds|minutes|hours|weeks|years] ==> days -:[prefix~seconds|minutes|days|weeks|years] ==> hours -:[prefix~seconds|minutes|days|hours|years] ==> weeks -:[prefix~seconds|minutes|days|hours|weeks] ==> years -now ==> 0 -block.timestamp ==> 0 -msg.value ==> 0 -msg.value ==> 1 -addmod ==> mulmod -mulmod ==> addmod -:[[expr]] ==> selfdestruct(msg.sender); -:[[expr]] ==> revert(); -call ==> delegatecall -delegatecall ==> call -call ==> callcode -callcode ==> call -delegatecall ==> callcode -callcode ==> delegatecall -:[[v1]] :[[v2]] ==> :[[v1]]:[[v2]] -if (:[expr]) ==> if (false) -if(:[expr]) ==> if(false) -if (:[expr]) ==> if (true) -if(:[expr]) ==> if(true) diff --git a/build/lib/universalmutator/comby/swift.rules b/build/lib/universalmutator/comby/swift.rules deleted file mode 100644 index b6d8cdc..0000000 --- a/build/lib/universalmutator/comby/swift.rules +++ /dev/null @@ -1,13 +0,0 @@ -var :[[a]] ==> let :[[a]] - -true ==> false -false ==> true - -[:[args]] ==> [] - -,:[[v1]], :[[v2]] ==> , - -:[[a]] && :[[b]] ==> :[[a]] && true -:[[a]] && :[[b]] ==> true && :[[b]] -:[[a]] || :[[b]] ==> :[[a]] || false -:[[a]] || :[[b]] ==> :[[a]] || false \ No newline at end of file diff --git a/build/lib/universalmutator/comby/universal.rules b/build/lib/universalmutator/comby/universal.rules deleted file mode 100644 index ab17a3b..0000000 --- a/build/lib/universalmutator/comby/universal.rules +++ /dev/null @@ -1,96 +0,0 @@ -DO_NOT_MUTATE ==> DO_NOT_MUTATE - -:[a]+:[b] ==> :[a]-:[b] -:[a]+:[b] ==> :[a]*:[b] -:[a]+:[b] ==> :[a]/:[b] -:[a]+:[b] ==> :[a]%:[b] - -:[a]-:[b] ==> :[a]+:[b] -:[a]-:[b] ==> :[a]*:[b] -:[a]-:[b] ==> :[a]/:[b] -:[a]-:[b] ==> :[a]%:[b] - -:[a]*:[b] ==> :[a]+:[b] -:[a]*:[b] ==> :[a]-:[b] -:[a]*:[b] ==> :[a]/:[b] -:[a]*:[b] ==> :[a]%:[b] - -:[a]/:[b] ==> :[a]+:[b] -:[a]/:[b] ==> :[a]-:[b] -:[a]/:[b] ==> :[a]*:[b] -:[a]/:[b] ==> :[a]%:[b] - -:[a]%:[b] ==> :[a]+:[b] -:[a]%:[b] ==> :[a]-:[b] -:[a]%:[b] ==> :[a]*:[b] -:[a]%:[b] ==> :[a]/:[b] - -:[a]!=:[b] ==> :[a]==:[b] -:[a]!=:[b] ==> :[a]<=:[b] -:[a]!=:[b] ==> :[a]>=:[b] -:[a]!=:[b] ==> :[a]>:[b] -:[a]!=:[b] ==> :[a]<:[b] - -:[a]==:[b] ==> :[a]!=:[b] -:[a]==:[b] ==> :[a]<=:[b] -:[a]==:[b] ==> :[a]>=:[b] -:[a]==:[b] ==> :[a]>:[b] -:[a]==:[b] ==> :[a]<:[b] - -:[a]>=:[b] ==> :[a]==:[b] -:[a]>=:[b] ==> :[a]!=:[b] -:[a]>=:[b] ==> :[a]<:[b] -:[a]>=:[b] ==> :[a]>:[b] - -:[a]<=:[b] ==> :[a]==:[b] -:[a]<=:[b] ==> :[a]!=:[b] -:[a]<=:[b] ==> :[a]<:[b] -:[a]<=:[b] ==> :[a]>:[b] - -:[a]<:[b] ==> :[a]>:[b] -:[a]<:[b] ==> :[a]==:[b] -:[a]<:[b] ==> :[a]<=:[b] -:[a]<:[b] ==> :[a]>=:[b] -:[a]<:[b] ==> :[a]!=:[b] - -:[a]>:[b] ==> :[a]<:[b] -:[a]>:[b] ==> :[a]==:[b] -:[a]>:[b] ==> :[a]>=:[b] -:[a]>:[b] ==> :[a]<=:[b] -:[a]>:[b] ==> :[a]!=:[b] - -:[a]+=:[b] ==> :[a]=+:[b] -:[a]-=:[b] ==> :[a]=-:[b] - -:[a]-- ==> :[a]++ -:[a]++ ==> :[a]-- - -:[neg~-]:[a] ==> :[a] - -:[a~\D]:[number~\d+]:[b~\D] ==> :[a]0:[b] -:[a~\D]:[number~\d+]:[b~\D] ==> :[a]1:[b] -:[a~\D]:[number~\d+]:[b~\D] ==> :[a]-1:[b] -:[a~\D]:[number~\d+]:[b~\D] ==> :[a](:[number]+1):[b] -:[a~\D]:[number~\d+]:[b~\D] ==> :[a](:[number]-1):[b] - -:[a]&&:[b] ==> :[a]||:[b] -:[a]||:[b] ==> :[a]&&:[b] -!:[a] ==> :[a] - -:[a]&:[b] ==> :[a]|:[b] -:[a]|:[b] ==> :[a]&:[b] - -:[s~[ \t]*]:[[a]]:[b]:[lf~[\n]] ==> :[s]:[a]:[b]:[lf]:[s]break;:[lf] -:[s~[ \t]*]:[[a]]:[b]:[lf~[\n]] ==> :[s]:[a]:[b]:[lf]:[s]continue;:[lf] - -":[str]" ==> "" - -while ==> if - -:[lead~[,\(\[\{]]:[v1~[^,=\)\}\]]+],:[v2~[^,=\)\}\]]+]:[end~[,\)\]\}]] ==> :[lead]:[v2],:[v1]:[end] -:[lead~[,\(\[\{]]:[name1~[^,=\)\}\]]+]=:[val1~[^,=\)\}\]]+],:[name2~[^,=\)\}\]]+]=:[val2~[^,=\)\}\]]+]:[end~[,\)\]\}]] ==> :[lead]:[name2]=:[val1],:[name1]=:[val2]:[end] - -min ==> max -max ==> min -begin ==> end -end ==> begin \ No newline at end of file diff --git a/build/lib/universalmutator/comby/vyper.rules b/build/lib/universalmutator/comby/vyper.rules deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/universalmutator/cpp_handler.py b/build/lib/universalmutator/cpp_handler.py deleted file mode 100644 index 2222de5..0000000 --- a/build/lib/universalmutator/cpp_handler.py +++ /dev/null @@ -1,4 +0,0 @@ -dumb = True - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - return "VALID" diff --git a/build/lib/universalmutator/fe_handler.py b/build/lib/universalmutator/fe_handler.py deleted file mode 100644 index b26d56c..0000000 --- a/build/lib/universalmutator/fe_handler.py +++ /dev/null @@ -1,45 +0,0 @@ -import glob -import os -import subprocess -import shutil - - -def extractOpcodes(text, filename): - return text - - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - outName = ".um.out." + str(os.getpid()) + ".opcodes" - if len(uniqueMutants) == 0: - shutil.copy(tmpMutantName, tmpMutantName + ".backup." + str(os.getpid())) - shutil.copy(sourceFile, tmpMutantName) - try: - shutil.rmtree(".tmp_mutant_fe") - except EnvironmentError: - pass - with open(outName, 'w') as file: - r = subprocess.call( - ["fe", tmpMutantName, "--emit", "yul", "--overwrite", "-o", ".tmp_mutant_fe"], stdout=file, stderr=file) - code = "" - for yulf in glob.glob(".tmp_mutant_fe/*/*.yul"): - with open(yulf, 'r') as file: - code += extractOpcodes(file.read(), tmpMutantName) - uniqueMutants[code] = 1 - try: - shutil.rmtree(".tmp_mutant_fe") - except EnvironmentError: - pass - with open(outName, 'w') as file: - r = subprocess.call(["fe", tmpMutantName, "--emit", - "yul", "--overwrite", "-o", ".tmp_mutant_fe"], stdout=file, stderr=file) - if r == 0: - code = "" - for yulf in glob.glob(".tmp_mutant_fe/*/*.yul"): - with open(yulf, 'r') as file: - code += extractOpcodes(file.read(), tmpMutantName) - if code in uniqueMutants: - uniqueMutants[code] += 1 - return "REDUNDANT" - uniqueMutants[code] = 1 - return "VALID" - return "INVALID" diff --git a/build/lib/universalmutator/findmissing.py b/build/lib/universalmutator/findmissing.py deleted file mode 100644 index 457b904..0000000 --- a/build/lib/universalmutator/findmissing.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import print_function -import glob -import sys - -from universalmutator import utils - - -def main(): - - f = sys.argv[1] - d1 = sys.argv[2] - d2 = sys.argv[3] - - fsplit = f.split(".") - pattern = "/" + fsplit[0] + ".mutant.*." + fsplit[-1] - d1files = glob.glob(d1 + pattern) - d2files = glob.glob(d2 + pattern) - d1contents = [] - d2contents = [] - d1all = [] - d2all = [] - for d1f in d1files: - with open(d1f, "r") as d1c: - r = d1c.read() - if r not in d1all: - d1all.append(r) - else: - print(d1f, "IS REDUNDANT!") - d1contents.append((d1f, r)) - for d2f in d2files: - with open(d2f, "r") as d2c: - r = d2c.read() - if r not in d2all: - d2all.append(r) - else: - print(d2f, "IS REDUNDANT!") - d2contents.append((d2f, r)) - - just1c = list(map(lambda x: x[1], d1contents)) - just2c = list(map(lambda x: x[1], d2contents)) - print("="*80) - for (mf, c) in d1contents: - if c not in just2c: - m = utils.readMutant(mf, f) - utils.show(m) - print("="*80) - for (mf, c) in d2contents: - if c not in just1c: - m = utils.readMutant(mf, f) - utils.show(m) - - -if __name__ == '__main__': - main() diff --git a/build/lib/universalmutator/fortran_handler.py b/build/lib/universalmutator/fortran_handler.py deleted file mode 100644 index 2222de5..0000000 --- a/build/lib/universalmutator/fortran_handler.py +++ /dev/null @@ -1,4 +0,0 @@ -dumb = True - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - return "VALID" diff --git a/build/lib/universalmutator/genmutants.py b/build/lib/universalmutator/genmutants.py deleted file mode 100644 index e7352a0..0000000 --- a/build/lib/universalmutator/genmutants.py +++ /dev/null @@ -1,623 +0,0 @@ -from __future__ import print_function -from tabulate import tabulate - -import os -import random -from re import T -import sys -import shutil -import subprocess - -from universalmutator import mutator - -from universalmutator import c_handler -from universalmutator import cpp_handler -from universalmutator import python_handler -from universalmutator import java_handler -from universalmutator import javascript_handler -from universalmutator import swift_handler -from universalmutator import rust_handler -from universalmutator import go_handler -from universalmutator import lisp_handler -from universalmutator import solidity_handler -from universalmutator import vyper_handler -from universalmutator import fe_handler -from universalmutator import r_handler -from universalmutator import fortran_handler - -def nullHandler(tmpMutantName, mutant, sourceFile, uniqueMutants): - return "VALID" - -def fastCheckLine(mutant, source, sourceFile, uniqueMutants, compileFile, handler, deadCodeLines, interestingLines, tmpMutantName, lineNo): - if compileFile is None: - mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants) - else: - mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants, compileFile=compileFile) - if mutantResult in ["VALID", "REDUNDANT"]: - deadCodeLines.append(lineNo) - print("LINE", str(lineNo) + ":", source[lineNo - 1][:-1], end=" ") - print("APPEARS TO BE COMMENT OR DEAD CODE, SKIPPING...") - else: - interestingLines.append(lineNo) - -def checkCombyDeadCode(deadCodeLines, mutant): - for i in range(mutant[3][0], mutant[3][1]+1): - if i not in deadCodeLines: - return False - return True - -def cmdHandler(tmpMutantName, mutant, sourceFile, uniqueMutants): - if "MUTANT" not in cmd: - # We asssume if the MUTANT isn't part of the command, - # we need to move it into place, before, e.g., make - backupName = sourceFile + ".um.backup." + str(os.getpid()) - shutil.copy(sourceFile, backupName) - shutil.copy(tmpMutantName, sourceFile) - try: - with open(".um.mutant_output." + str(os.getpid()), 'w') as file: - r = subprocess.call([cmd.replace("MUTANT", tmpMutantName)], - shell=True, stderr=file, stdout=file) - if r == 0: - return "VALID" - return "INVALID" - finally: - # If we moved the mutant in, restore original - if "MUTANT" not in cmd: - shutil.copy(backupName, sourceFile) - - -def toGarbage(code): - newCode = "" - for c in code: - if c == "(": - newCode += " " - elif c.isspace() or c in ["*", "/", "#", "-"]: - newCode += c - else: - newCode += "Q" - return newCode - -cmd = None - -def main(): - global cmd - - try: - import custom_handler - except BaseException: - pass - - args = sys.argv - - languages = {".c": "c", - ".h": "c", - ".cpp": "cpp", - ".c++": "cpp", - ".cc": "cpp", - ".hpp": "cpp", - ".py": "python", - ".java": "java", - ".js": "javascript", - ".ts": "javascript", - ".swift": "swift", - ".rs": "rust", - ".go": "go", - ".lisp": "lisp", - ".lsp": "lisp", - ".f": "fortran", - ".f90": "fortran", - ".for": "fortran", - ".R": "r", - ".sol": "solidity", - ".vy": "vyper", - ".fe": "fe"} - - print("*** UNIVERSALMUTATOR ***") - - if ("--help" in args) or (len(sys.argv) < 2): - if len(sys.argv) < 2: - print("ERROR: mutate requires at least one argument (a file to mutate)\n") - print("USAGE: mutate [] [ ...]", - "[--noCheck] [--cmd ] [--mutantDir ]", - "[--lines [--tstl]] [--mutateTestCode] [--mutateBoth]", - "[--ignore ] [--compile ] [--noFastCheck] [--swap]", - "[--redundantOK] [--showRules] [--only ]") - print() - print(" --noCheck: skips compilation/comparison and just generates mutant files") - print(" --cmd executes command string, replacing MUTANT with the mutant name, and uses return code") - print(" to determine mutant validity") - print(" --mutantDir: directory to put generated mutants in; defaults to current directory") - print(" --lines: only generate mutants for lines contained in ") - print(" --tstl: is TSTL output") - print(" --mutateInStrings: mutate inside strings (not just turn to empty string)") - print(" --mutateTestCode: mutate only test code") - print(" --mutateBoth: mutate both test and normal code") - print(" --ignore : ignore lines matching patterns in ") - print(" --compile : compile instead of source (solidity handler only)") - print(" --comby: use comby as the method of mutating code") - print(" --noFastCheck: do not use fast dead code/comment detection heuristic") - print(" --swap: also try adjacent-code swaps") - print(" --redundantOK: keep redundant mutants (for compiler output issues)") - print(" --showRules: show rule source used to generate each mutant") - print(" --only : only use rule file ") - print(" --printStat: print stats for the rules and generated mutants into files") - print() - print("Currently supported languages: ", ", ".join(list(set(languages.values())))) - print("If not supplying a command to compile/build, you should use --noCheck for C, C++,") - print("javascript, and other languages with only a default handler.") - sys.exit(0) - - noCheck = False - if "--noCheck" in args: - noCheck = True - args.remove("--noCheck") - - comby = False - if "--comby" in args: - comby = True - args.remove("--comby") - - redundantOK = False - if "--redundantOK" in args: - redundantOK = True - args.remove("--redundantOK") - - showRules = False - if "--showRules" in args: - showRules = True - args.remove("--showRules") - - mutateInStrings = False - if "--mutateInStrings" in args: - mutateInStrings = True - args.remove("--mutateInStrings") - - mutateTestCode = False - if "--mutateTestCode" in args: - mutateTestCode = True - args.remove("--mutateTestCode") - - mutateBoth = False - if "--mutateBoth" in args: - mutateBoth = True - args.remove("--mutateBoth") - - noFastCheck = False - if "--noFastCheck" in args: - noFastCheck = True - args.remove("--noFastCheck") - - doSwaps = False - if "--swap" in args: - doSwaps = True - args.remove("--swap") - - tstl = False - if "--tstl" in args: - tstl = True - args.remove("--tstl") - - fuzz = False - if "--fuzz" in args: - fuzz = True - args.remove("--fuzz") - - printStat = False - if "--printStat" in args: - printStat = True - args.remove("--printStat") - - cmd = None - try: - cmdpos = args.index("--cmd") - except ValueError: - cmdpos = -1 - - if cmdpos != -1: - cmd = args[cmdpos + 1] - args.remove("--cmd") - args.remove(cmd) - - sourceFile = args[1] - ending = "." + sourceFile.split(".")[-1] - - lineFile = None - try: - linepos = args.index("--lines") - except ValueError: - linepos = -1 - - if linepos != -1: - lineFile = args[linepos + 1] - args.remove("--lines") - args.remove(lineFile) - - if lineFile is not None: - with open(lineFile) as file: - if not tstl: - lines = list(map(int, file.read().split())) - else: - lines = [] - for l in file: - if "LINES" in l: - if sourceFile not in l: - continue - db = l.split("[")[1] - d = db[:-2].split(",") - for line in d: - lines.append(int(line)) - - mdir = "." - try: - mdirpos = args.index("--mutantDir") - except ValueError: - mdirpos = -1 - - if mdirpos != -1: - mdir = args[mdirpos + 1] - args.remove("--mutantDir") - args.remove(mdir) - if mdir[-1] != "/": - mdir += "/" - - ignoreFile = None - try: - ignorepos = args.index("--ignore") - except ValueError: - ignorepos = -1 - - if ignorepos != -1: - ignoreFile = args[ignorepos + 1] - args.remove("--ignore") - args.remove(ignoreFile) - - compileFile = None - try: - compilepos = args.index("--compile") - except ValueError: - compilepos = -1 - - if compilepos != -1: - compileFile = args[compilepos + 1] - args.remove("--compile") - args.remove(compileFile) - - ignorePatterns = [] - if ignoreFile is not None: - print("IGNORING PATTERNS DEFINED IN", ignoreFile) - with open(ignoreFile, 'r') as ignoref: - for l in ignoref: - ignorePatterns.append(l[:-1]) - - handlers = {"python": python_handler, - "c": c_handler, - "c++": cpp_handler, - "cpp": cpp_handler, - "java": java_handler, - "javascript": javascript_handler, - "swift": swift_handler, - "rust": rust_handler, - "r": r_handler, - "fortran": fortran_handler, - "go": go_handler, - "lisp": lisp_handler, - "solidity": solidity_handler, - "vyper": vyper_handler, - "fe": fe_handler} - - cLikeLanguages = [ - "c", - "java", - "javascript", - "swift", - "cpp", - "c++", - "rust", - "solidity", - "go"] - - try: - handlers["custom"] == custom_handler - except BaseException: - pass - - sourceFile = args[1] - base = (".".join((sourceFile.split(".")[:-1]))).split("/")[-1] - ending = "." + sourceFile.split(".")[-1] - - if "--only" not in args: - if len(args) < 3: - try: - language = languages[ending] - except KeyError: - language = "none" - otherRules = [] - else: - if ".rules" in args[2]: - language = languages[ending] - otherRules = args[2:] - else: - language = args[2] - otherRules = args[3:] - - if language not in handlers: - if language.lower() in handlers: - language = language.lower() - - if language in cLikeLanguages: - otherRules.append("c_like.rules") - - if language == "vyper": - otherRules.append("python.rules") - otherRules.append("solidity.rules") - - if language == "fe": - otherRules.append("python.rules") - otherRules.append("solidity.rules") - - rules = ["universal.rules", language + ".rules"] + otherRules - if fuzz: - if language == "none": - fuzzRules = ["universal.rules", "c_like.rules", "python.rules", "vyper.rules", "solidity.rules"] - rules = list(set(fuzzRules + rules)) - else: - onlyPos = args.index("--only") - rules = [args[onlyPos + 1]] - if args[2] != "--only": - language = args[2] - else: - try: - language = languages[ending] - except KeyError: - language = "none" - if language not in handlers: - if language.lower() in handlers: - language = language.lower() - - source = [] - - with open(sourceFile, 'r') as file: - for line in file: - # remove non-ascii characters (comby issue) - line_processed = line.encode('ascii', 'ignore').decode() - source.append(line_processed) - - mutants = [] - - if comby: - mutants = mutator.mutants_comby(source, ruleFiles=rules, mutateTestCode=mutateTestCode, mutateBoth=mutateBoth, - ignorePatterns=ignorePatterns, ignoreStringOnly=not mutateInStrings, fuzzing=fuzz, language=ending) - else: - mutants = mutator.mutants_regexp(source, ruleFiles=rules, mutateTestCode=mutateTestCode, mutateBoth=mutateBoth, - ignorePatterns=ignorePatterns, ignoreStringOnly=not mutateInStrings, fuzzing=fuzz) - if fuzz: - if len(mutants) == 0: - sys.exit(255) - mutants = [random.choice(mutants)] # Just pick one - - print(len(mutants), "MUTANTS GENERATED BY RULES") - - validMutants = [] - invalidMutants = [] - redundantMutants = [] - uniqueMutants = {} - sourceJoined = '' - - if comby: - noFastCheck = True - - dumbHandler = False - if not noCheck: - if cmd is not None: - handler = cmdHandler - elif language == "none": - handler = nullHandler - else: - try: - if handlers[language].dumb: - noFastCheck = True - dumbHandler = True - except BaseException: - pass - handler = handlers[language].handler - else: - handler = nullHandler - noFastCheck = True - - deadCodeLines = [] - interestingLines = [] - - tmpMutantName = ".tmp_mutant." + str(os.getpid()) + ending - mutantNo = 0 - for mutant in mutants: - if (lineFile is not None) and mutant[0] not in lines: - # skip if not a line to mutate - continue - if not noFastCheck: - if comby: - checkLines = [] - for i in range(mutant[3][0], mutant[3][1] + 1): - if i not in deadCodeLines or i not in interestingLines: - checkLines.append(i) - for lineNo in checkLines: - fastCheckMutant = (lineNo, toGarbage(source[lineNo - 1])) - mCreated = mutator.makeMutant(source, fastCheckMutant, tmpMutantName) - if mCreated: - fastCheckLine(mutant, source, sourceFile, uniqueMutants, compileFile, handler, deadCodeLines, interestingLines, tmpMutantName, lineNo) - if checkCombyDeadCode(deadCodeLines, mutant): - continue - else: - if (mutant[0] not in interestingLines) and (mutant[0] not in deadCodeLines): - fastCheckMutant = (mutant[0], toGarbage(source[mutant[0] - 1])) - mCreated = mutator.makeMutant(source, fastCheckMutant, tmpMutantName) - if mCreated: - fastCheckLine(mutant, source, sourceFile, uniqueMutants, compileFile, handler, deadCodeLines, interestingLines, tmpMutantName, mutant[0]) - if mutant[0] in deadCodeLines: - continue - - if comby: - sourceJoined = ''.join(source) - print("PROCESSING MUTANT:", - "range" + str(mutant[0]) + ":", sourceJoined[mutant[0][0]:mutant[0][1]].replace("\n", "\\n"), " ==> ", mutant[1], end="...") - else: - print("PROCESSING MUTANT:", - str(mutant[0]) + ":", source[mutant[0] - 1][:-1], " ==> ", mutant[1][:-1], end="...") - if (not comby) and showRules: - print("(FROM:", mutant[2][1], end=")...") - - if comby: - mCreated = mutator.makeMutantComby(sourceJoined, mutant, tmpMutantName) - else: - mCreated = mutator.makeMutant(source, mutant, tmpMutantName) - if not mCreated: - print("REDUNDANT (SOURCE COPY!)") - redundantMutants.append(mutant) - continue - - if compileFile is None: - mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants) - else: - mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants, compileFile=compileFile) - print(mutantResult, end=" ") - mutantName = mdir + base + ".mutant." + str(mutantNo) + ending - if fuzz: - mutantName = mdir + "fuzz.out" - if (mutantResult == "VALID") or (mutantResult == "REDUNDANT" and redundantOK): - print("[written to", mutantName + "]", end=" ") - shutil.copy(tmpMutantName, mutantName) - validMutants.append(mutant) - mutantNo += 1 - elif mutantResult == "INVALID": - invalidMutants.append(mutant) - elif mutantResult == "REDUNDANT": - redundantMutants.append(mutant) - print() - sys.stdout.flush() - - if doSwaps: - print("TRYING CODE SWAPS...") - swapList = [] - for lineNo in range(len(source)): - if lineNo + 1 in deadCodeLines: - continue - swapList.append(lineNo) - for i in range(0, len(swapList)-1): - a = swapList[i] - b = swapList[i+1] - mutant = [a + 1] # Only the line is valid here - print("TRYING TO SWAP LINES", a + 1, "AND", b + 1, end="...") - newSource = source[:a] - newSource.append(source[b]) - newSource += source[a+1:b] - newSource.append(source[a]) - newSource += source[b+1:] - with open(tmpMutantName, 'w') as file: - for line in newSource: - file.write(line) - if compileFile is None: - mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants) - else: - mutantResult = handler(tmpMutantName, mutant, sourceFile, uniqueMutants, compileFile=compileFile) - print(mutantResult, end=" ") - mutantName = mdir + base + ".mutant." + str(mutantNo) + ending - if (mutantResult == "VALID") or (mutantResult == "REDUNDANT" and redundantOK): - print("[written to", mutantName + "]", end=" ") - shutil.copy(tmpMutantName, mutantName) - validMutants.append(mutant) - mutantNo += 1 - elif mutantResult == "INVALID": - invalidMutants.append(mutant) - elif mutantResult == "REDUNDANT": - redundantMutants.append(mutant) - print() - sys.stdout.flush() - - print(len(validMutants), "VALID MUTANTS") - print(len(invalidMutants), "INVALID MUTANTS") - print(len(redundantMutants), "REDUNDANT MUTANTS") - - totalMutants = len(validMutants) + len(invalidMutants) + len(redundantMutants) - valid_rate = 0 if totalMutants == 0 else (len(validMutants) * 100.0)/totalMutants - print(f"Valid Percentage: {valid_rate}%") - - (rules, _, _) = mutator.parseRules(rules, comby= comby) - - if printStat: - source = sourceJoined if comby else None - printMutantsStat((validMutants, invalidMutants, redundantMutants), source) - printRulesStat(rules, validMutants, invalidMutants) - - if dumbHandler: - print() - print("*"*80) - print("WARNING: because the handler does not compile and so has no pruning support,") - print("all mutants were considered valid. Consider using --cmd to build the target!") - print("*"*80) - print() - - try: - os.remove(tmpMutantName) - except BaseException: - pass - -def printMutantsStat(mutants, source = None): - def dumpToFile(fileName, mutants): - fis = open(fileName, "w") - i = 0 - for mutant in mutants: - i += 1 - sys.stdout.flush() - - fis.write(f"{i}.\n") - fis.write(mutant[2][0]) - fis.write('\n') - if source is not None: - fis.write("source:\n") - fis.write(source[mutant[0][0]:mutant[0][1]]) - fis.write('\n') - fis.write("mutant:\n") - fis.write(mutant[1]) - fis.write('\n\n') - fis.close() - - validMutants, invalidMutants, redundantMutants = mutants - dumpToFile('valid_mutants.txt', validMutants) - dumpToFile('invalid_mutants.txt', invalidMutants) - dumpToFile('redundant_mutants.txt', redundantMutants) - -def printRulesStat(rules, validMutants, invalidMutants): - valid_cnt = {} - invalid_cnt = {} - - for mutant in validMutants: - lhs, rhs = mutant[-1] - if (lhs,rhs) not in valid_cnt: - valid_cnt[(lhs,rhs)] = 0 - valid_cnt[(lhs,rhs)] += 1 - - for mutant in invalidMutants: - lhs, rhs = mutant[-1] - if (lhs,rhs) not in invalid_cnt: - invalid_cnt[(lhs,rhs)] = 0 - invalid_cnt[(lhs,rhs)] += 1 - - fis = open("rules_count.txt", "w") - i = 0 - table = [] - table.append(["#","Rule","No. of Valids", "No. of Invalids"]) - - for ((lhs, rhs), _) in rules: - if (lhs,rhs) not in valid_cnt: - valid_cnt[(lhs,rhs)] = 0 - if (lhs,rhs) not in invalid_cnt: - invalid_cnt[(lhs,rhs)] = 0 - i += 1 - - table.append([f'{i}',f'{lhs} ==> {rhs}', f'{valid_cnt[(lhs,rhs)]}', f'{invalid_cnt[(lhs,rhs)]}']) - - fis.write(tabulate(table,tablefmt="grid")) - sys.stdout.flush() - fis.close() - -if __name__ == '__main__': - main() diff --git a/build/lib/universalmutator/go_handler.py b/build/lib/universalmutator/go_handler.py deleted file mode 100644 index 2222de5..0000000 --- a/build/lib/universalmutator/go_handler.py +++ /dev/null @@ -1,4 +0,0 @@ -dumb = True - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - return "VALID" diff --git a/build/lib/universalmutator/intersect.py b/build/lib/universalmutator/intersect.py deleted file mode 100644 index e960dd2..0000000 --- a/build/lib/universalmutator/intersect.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import print_function -import sys - - -def main(): - - args = sys.argv - - if ("--help" in args) or (len(sys.argv) < 2): - if len(sys.argv) < 2: - print("ERROR: intersect_mutants requires at least three arguments\n") - print("USAGE: intersect_mutants ") - sys.exit(0) - - infile1 = sys.argv[1] - infile2 = sys.argv[2] - outfile = sys.argv[3] - - infile1_mutants = [] - with open(infile1, 'r') as if1: - for line in if1: - infile1_mutants.append(line.split()[0]) - - with open(outfile, 'w') as outf: - with open(infile2, 'r') as if2: - for line in if2: - m = line.split()[0] - if m in infile1_mutants: - outf.write(line) - - -if __name__ == '__main__': - main() diff --git a/build/lib/universalmutator/java_handler.py b/build/lib/universalmutator/java_handler.py deleted file mode 100644 index 6257778..0000000 --- a/build/lib/universalmutator/java_handler.py +++ /dev/null @@ -1,26 +0,0 @@ -import os -import subprocess -import shutil - - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - backupName = sourceFile + ".um.backup." + str(os.getpid()) - classFile = sourceFile.replace(".java", ".class") - classBackupName = classFile + ".um.backup" + str(os.getpid()) - try: - shutil.copy(sourceFile, backupName) - if os.path.exists(classFile): - shutil.copy(classFile, classBackupName) - shutil.copy(tmpMutantName, sourceFile) - with open(".um.mutant_output" + str(os.getpid()), 'w') as file: - r = subprocess.call(["javac", sourceFile], - stdout=file, stderr=file) - finally: - shutil.copy(backupName, sourceFile) - os.remove(backupName) - if os.path.exists(classBackupName): - shutil.copy(classBackupName, classFile) - os.remove(classBackupName) - if r == 0: - return "VALID" - return "INVALID" diff --git a/build/lib/universalmutator/javascript_handler.py b/build/lib/universalmutator/javascript_handler.py deleted file mode 100644 index 2222de5..0000000 --- a/build/lib/universalmutator/javascript_handler.py +++ /dev/null @@ -1,4 +0,0 @@ -dumb = True - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - return "VALID" diff --git a/build/lib/universalmutator/lisp_handler.py b/build/lib/universalmutator/lisp_handler.py deleted file mode 100644 index 2222de5..0000000 --- a/build/lib/universalmutator/lisp_handler.py +++ /dev/null @@ -1,4 +0,0 @@ -dumb = True - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - return "VALID" diff --git a/build/lib/universalmutator/mutator.py b/build/lib/universalmutator/mutator.py deleted file mode 100644 index b4e65b1..0000000 --- a/build/lib/universalmutator/mutator.py +++ /dev/null @@ -1,325 +0,0 @@ -from __future__ import print_function -import re -import sys -import random -from comby import Comby -import os -from json.decoder import JSONDecodeError - -# Python 3.9+ has importlib.resources with the modern files() API. -# Older Pythons (< 3.9) fall back to pkg_resources from setuptools. -if sys.version_info >= (3, 9): - import importlib.resources as _importlib_resources - _use_importlib = True -else: - try: - import importlib.resources as _importlib_resources - _use_importlib = True - except ImportError: - import pkg_resources as _pkg_resources - _use_importlib = False - - -def _open_package_resource(package, resource_path): - """Open a package data file, using importlib.resources on Python 3.9+ - and falling back to pkg_resources on older versions.""" - if _use_importlib: - parts = resource_path.replace("\\", "/").split("/") - subpackage = package + "." + ".".join(parts[:-1]) if len(parts) > 1 else package - filename = parts[-1] - ref = _importlib_resources.files(subpackage).joinpath(filename) - return ref.open("rb") - else: - return _pkg_resources.resource_stream(package, resource_path) - - -def parseRules(ruleFiles, comby=False): - rulesText = [] - - for ruleFile in ruleFiles: - if ".rules" not in ruleFile: - ruleFile += ".rules" - try: - if comby: - rulePath = os.path.join('comby', ruleFile) - else: - rulePath = os.path.join('static', ruleFile) - with _open_package_resource('universalmutator', rulePath) as builtInRule: - for line in builtInRule: - line = line.decode() - rulesText.append((line, "builtin:" + ruleFile)) - except BaseException: - print("FAILED TO FIND RULE", ruleFile, "AS BUILT-IN...") - try: - with open(ruleFile, 'r') as file: - for l in file: - rulesText.append((l, ruleFile)) - except BaseException: - print("COULD NOT FIND RULE FILE", ruleFile + "! SKIPPING...") - - rules = [] - ignoreRules = [] - skipRules = [] - ruleLineNo = 0 - - for (r, ruleSource) in rulesText: - ruleLineNo += 1 - - # remove all leading and trailing white space - line = r.strip() - - # check for blank lines - if line == "": - # ignore blank lines - continue - - # handle comments - if line.startswith("#") and "==>" not in line: - # ignore comments '#' - continue - - # check for disabled rules - if line.startswith("#DISABLED:"): - # ignore disabled rules - continue - - # check and parse valid rules - if " ==> " in line: - s = line.split(" ==> ") - elif " ==>" in line: - s = line.split(" ==>") - else: - # otherwise it's a invalid line and warn user - print("*" * 60) - print("WARNING:") - print("RULE:", line, "FROM", ruleSource) - print("DOES NOT MATCH EXPECTED FORMAT, AND SO WAS IGNORED") - print("*" * 60) - continue - - # End of possible fix - - if comby: - lhs = s[0] - lhs = lhs.rstrip() # Trailing whitespace in rule file will be treated significantly unless stripped, so strip it. If matching trailing whitespace is desired, then regex holes should be used. - else: - try: - lhs = re.compile(s[0]) - except BaseException: - print("*" * 60) - print("FAILED TO COMPILE RULE:", r, "FROM", ruleSource) - print("*" * 60) - continue - if (len(s[1]) > 0) and (s[1][-1] == "\n"): - rhs = s[1][:-1] - else: - rhs = s[1] - if rhs == "DO_NOT_MUTATE": - ignoreRules.append(lhs) - elif rhs == "SKIP_MUTATING_REST": - skipRules.append(lhs) - else: - rules.append(((lhs, rhs), (r, ruleSource + ":" + str(ruleLineNo)))) - - return (rules, ignoreRules, skipRules) - - - -def mutants_comby(source, ruleFiles=None, mutateTestCode=False, mutateBoth=False, - ignorePatterns=None, ignoreStringOnly=False, fuzzing=False, language=".generic"): - if ruleFiles is None: - ruleFiles = ["universal.rules"] - comby = Comby() - print("MUTATING WITH RULES (COMBY):", ", ".join(ruleFiles)) - (rules, ignoreRules, _) = parseRules(ruleFiles, True) - for lhs in ignorePatterns: - ignoreRules.append(lhs) - source = ''.join(source) - mutants = [] - - # Lines that match with DO_NOT_MUTATE and other ignore rules will be skipped - ignoreLines = set() - for lhs in ignoreRules: - for match in comby.matches(source, lhs, language=language): - lineRange = (match.location.start.line, match.location.stop.line) - for line in range(lineRange[0],lineRange[1]+1): - ignoreLines.add(line) - - # Instead of line-by-line x rule-by-rule iterate rule-by-rule x match-by-match. - for ((lhs, rhs), ruleUsed) in rules: - try: - for match in comby.matches(source, lhs, language=language): - environment = {} - for entry in match.environment: - environment[entry] = match.environment.get(entry).fragment - mutant = comby.substitute(rhs, environment) - substitutionRange = (match.location.start.offset, match.location.stop.offset) - lineRange = (match.location.start.line, match.location.stop.line) - - # Check if the match contains an ignoreLine - skipMutant = False - for line in range(lineRange[0],lineRange[1]+1): - if line in ignoreLines: - skipMutant = True - break - - if not skipMutant: - mutants.append((substitutionRange, mutant, ruleUsed, lineRange, (lhs,rhs))) - except JSONDecodeError: - continue - except Exception as e: - print(f"WARNING: Got exception {e} running rule {ruleUsed}") - continue - return mutants - -def mutants_regexp(source, ruleFiles=None, mutateTestCode=False, mutateBoth=False, - ignorePatterns=None, ignoreStringOnly=False, fuzzing=False): - if ruleFiles is None: - ruleFiles = ["universal.rules"] - print("MUTATING WITH RULES:", ", ".join(ruleFiles)) - - (rules, ignoreRules, skipRules) = parseRules(ruleFiles) - - for p in ignorePatterns: - try: - lhs = re.compile(p) - except BaseException: - print("*" * 60) - print("FAILED TO COMPILE IGNORE PATTERN:", p) - print("*" * 60) - continue - ignoreRules.append(lhs) - - mutants = [] - produced = {} - lineno = 0 - stringSkipped = 0 - inTestCode = False - targetLine = None - if fuzzing: - # Pick a random target line, ignore others - if len(source) == 0: - return [] - targetLine = random.randrange(1, len(source) + 1) - for l in source: - lineno += 1 - if fuzzing and (lineno != targetLine): - continue - if inTestCode: - if "@END_TEST_CODE" in l: - inTestCode = False - if (not mutateTestCode) and (not mutateBoth): - continue - else: - if "@BEGIN_TEST_CODE" in l: - inTestCode = True - continue - if mutateTestCode and (not mutateBoth): - continue - skipLine = False - for lhs in ignoreRules: - if lhs.search(l): - skipLine = True - break - if skipLine: - continue - abandon = False - for ((lhs, rhs), ruleUsed) in rules: - skipPos = len(l) - for skipRule in skipRules: - skipp = skipRule.search(l, 0) - if skipp and (skipp.start() < skipPos): - skipPos = skipp.start() - - - p = lhs.search(l, 0) - - # skip mutating if match occurs at index >= skipPos - while p and (p.start() < skipPos): - try: - mutant = l[:p.start()] + lhs.sub(rhs, l[p.start():], count=1) - except KeyboardInterrupt: - raise - except Exception as e: - print("WARNING: Applying mutation raised an exception:", e) - print("Abandoning mutation of line", lineno) - abandon = True - break - if mutant[-1] != "\n": - mutant += "\n" - skipDueToString = False - if ignoreStringOnly: - noStringsOrig = "" - inString = False - slen = 0 - for spos in range(0, len(l)): - if not inString: - noStringsOrig += l[spos] - if l[spos] == '"': - inString = True - slen = 0 - else: - slen += 1 - if l[spos] == '"': - noStringsOrig += str(slen > 2) - noStringsOrig += l[spos] - inString = False - noStringsMutant = "" - inString = False - slen = 0 - for spos in range(0, len(mutant)): - if not inString: - noStringsMutant += mutant[spos] - if mutant[spos] == '"': - inString = True - slen = 0 - else: - slen += 1 - if mutant[spos] == '"': - noStringsMutant += str(slen > 2) - noStringsMutant += mutant[spos] - inString = False - if noStringsOrig == noStringsMutant: - skipDueToString = True - stringSkipped += 1 - if (mutant != l) and ((lineno, mutant) not in produced) and (not skipDueToString): - mutants.append((lineno, mutant, ruleUsed, (lhs,rhs))) - produced[(lineno, mutant)] = True - - p = lhs.search(l, p.start()+1) #search from the next position of the current match - if abandon: - break - - if stringSkipped > 0: - print("SKIPPED", stringSkipped, "MUTANTS ONLY CHANGING STRING LITERALS") - return mutants - - -def makeMutant(source, mutant, path): - lineModified = mutant[0] - newCode = mutant[1] - if source[lineModified - 1] == newCode: - print("** WARNING: SKIPPING GENERATING IDENTICAL MUTANT **") - print(mutant) - return False - with open(path, 'w') as file: - lineno = 0 - for l in source: - lineno += 1 - if lineno != lineModified: - file.write(l) - else: - file.write(newCode) - return True - -def makeMutantComby(source, mutant, path): - sourceBeforeFragment = source[:mutant[0][0]] - sourceAfterFragment = source[mutant[0][1]:] - mutantSource = sourceBeforeFragment + mutant[1] + sourceAfterFragment - if mutantSource == source: - print("** WARNING: SKIPPING GENERATING IDENTICAL MUTANT **") - print(mutant) - return False - with open(path, 'w') as file: - file.write(mutantSource) - return True diff --git a/build/lib/universalmutator/prioritize.py b/build/lib/universalmutator/prioritize.py deleted file mode 100644 index 003efa4..0000000 --- a/build/lib/universalmutator/prioritize.py +++ /dev/null @@ -1,131 +0,0 @@ -from __future__ import print_function -import glob -import sys - -from universalmutator import utils - - -def main(): - - args = sys.argv - - if ("--help" in args) or (len(sys.argv) < 3): - if len(sys.argv) < 3: - print("ERROR: prioritize_mutants requires at least two arguments\n") - print("USAGE: prioritize_mutants [N] [--cutoff ]", end="") - print("[--mutantDir ] [--sourceDir ]") - print(" --verbose: produce verbose output") - print(" --noSDPriority: do not prioritize statement deletions over other mutants") - print(" --mutantDir: directory with all mutants; defaults to current directory") - print(" --sourceDir: directory of source files; defaults to current directory") - print(" --cutoff: if minimum distance is less than , stop") - sys.exit(0) - - infile = sys.argv[1] - outfile = sys.argv[2] - - infiles = glob.glob(infile) - - verbose = False - if "--verbose" in args: - args.remove("--verbose") - verbose = True - - noSDPriority = False - if "--noSDPriority" in args: - args.remove("--noSDPriority") - noSDPriority = True - - mdir = "." - try: - mdirpos = args.index("--mutantDir") - except ValueError: - mdirpos = -1 - - if mdirpos != -1: - mdir = args[mdirpos + 1] - args.remove("--mutantDir") - args.remove(mdir) - if mdir[-1] != "/": - mdir += "/" - - sdir = "." - try: - sdirpos = args.index("--sourceDir") - except ValueError: - sdirpos = -1 - - if sdirpos != -1: - sdir = args[sdirpos + 1] - args.remove("--sourceDir") - args.remove(sdir) - if sdir[-1] != "/": - sdir += "/" - - cutoff = 0.0 - try: - cutoffpos = args.index("--cutoff") - except ValueError: - cutoffpos = -1 - - if cutoffpos != -1: - cutoff = args[cutoffpos + 1] - args.remove("--cutoff") - args.remove(cutoff) - cutoff = float(cutoff) - - N = -1 - if len(args) >= 4: - N = int(args[3]) - - mutants = [] - for f in infiles: - with open(f, 'r') as mfile: - for line in mfile: - name = line[:-1] - suffix = "." + name.split(".")[-1] - mpart = ".mutant." + name.split(".mutant.")[1] - original = name.replace(mpart, suffix) - try: - mutants.append(utils.readMutant(name, original, mutantDir=mdir, sourceDir=sdir)) - except AssertionError: - print("WARNING:", name, "AND SOURCE ARE IDENTICAL") - print("READ", len(mutants), "MUTANTS") - - if N == -1: - N = len(mutants) - - sdmutants = [] - if not noSDPriority: - print("IDENTIFYING STATEMENT DELETION MUTANTS") - sdmutants = list(filter(utils.isStatementDeletion, mutants)) - for m in sdmutants: - mutants.remove(m) - print("PRIORITIZING", len(sdmutants), "STATEMENT DELETIONS") - if len(sdmutants) > 0: - ranking = utils.FPF(sdmutants, N, cutoff=cutoff, verbose=verbose, mutantDir=mdir, - sourceDir=sdir) - with open(outfile, 'w') as outf: - for (m, _) in ranking: - mname = m[0] - outf.write(mname + "\n") - else: - with open(outfile, 'w') as outf: - pass - print() - else: - with open(outfile, 'w') as outf: - pass - - print("PRIORITIZING", int(N) - len(sdmutants), "MUTANTS") - - ranking = utils.FPF(mutants, N - len(sdmutants), cutoff=cutoff, verbose=verbose, avoid=sdmutants, - mutantDir=mdir, sourceDir=sdir) - with open(outfile, 'a') as outf: - for (m, _) in ranking: - mname = m[0] - outf.write(mname + "\n") - - -if __name__ == '__main__': - main() diff --git a/build/lib/universalmutator/prune.py b/build/lib/universalmutator/prune.py deleted file mode 100644 index 9a81009..0000000 --- a/build/lib/universalmutator/prune.py +++ /dev/null @@ -1,136 +0,0 @@ -from __future__ import print_function -import re -import sys - -from universalmutator import utils - - -def main(): - - args = sys.argv - - if ("--help" in args) or (len(sys.argv) < 2): - if len(sys.argv) < 2: - print("ERROR: show_mutants requires at least one argument\n") - print("USAGE: prune_mutants [--mutantDir ] [--sourceDir ]") - print(" --mutantDir: directory with all mutants; defaults to current directory") - print(" --sourceDir: directory of source files; defaults to current directory") - sys.exit(0) - - infile = sys.argv[1] - outfile = sys.argv[2] - config = sys.argv[3] - - mdir = "." - try: - mdirpos = args.index("--mutantDir") - except ValueError: - mdirpos = -1 - - if mdirpos != -1: - mdir = args[mdirpos + 1] - args.remove("--mutantDir") - args.remove(mdir) - if mdir[-1] != "/": - mdir += "/" - - sdir = "." - try: - sdirpos = args.index("--sourceDir") - except ValueError: - sdirpos = -1 - - if sdirpos != -1: - sdir = args[sdirpos + 1] - args.remove("--sourceDir") - args.remove(sdir) - if sdir[-1] != "/": - sdir += "/" - - mutants = [] - with open(infile, 'r') as mfile: - for line in mfile: - name = line[:-1] - suffix = "." + name.split(".")[-1] - mpart = ".mutant." + name.split(".mutant.")[1] - original = sdir + name.replace(mpart, suffix) - mutants.append(utils.readMutant(name, original, mutantDir=mdir)) - print("READ", len(mutants), "MUTANTS") - - constraints = {} - constraints["orig"] = [] - constraints["mutant"] = [] - constraints["change"] = [] - constraints["function"] = [] - constraints["contract"] = [] - constraints["source"] = [] - constraints["line"] = [] - for v in constraints.keys(): - constraints["!" + v] = [] - for v in constraints.keys(): - constraints[v + "_RE"] = [] - - baseTypes = set() - - with open(config, 'r') as cfile: - for line in cfile: - r = line.rstrip('\n') - (ctype, crule) = r.split(": ", 1) - if ctype not in constraints: - print("INVALID CONSTRAINT TYPE IN PRUNING RULE:", line) - if "_RE" in ctype: - crule = re.compile(crule) - baseTypes.add(ctype.replace("_RE", "").replace("!", "")) - constraints[ctype].append(crule) - - pruned = [] - - properties = {} - for m in mutants: - prunedYet = False - (mfile, sourcefile, pos, orig, mutant) = m - properties["orig"] = orig - properties["mutant"] = mutant - properties["line"] = pos - properties["source"] = sourcefile - if "change" in baseTypes: - properties["change"] = utils.change(m) - if "function" in baseTypes: - properties["function"] = utils.solidityFunction(m) - if "contract" in baseTypes: - properties["contract"] = utils.solidityContract(m) - for ctype in constraints: - field = ctype.replace("_RE", "").replace("!", "") - negated = ctype[0] == "!" - regexp = "_RE" in ctype - for c in constraints[ctype]: - if "line" in ctype: - cstart = int(c.split("-")[0]) - cend = int(c.split("-")[1]) - matched = (properties[field] >= cstart) and (properties[field] <= cend) - elif not regexp: - matched = c in properties[field] - else: - matched = regexp.search(c, properties[field]) - result = not ((matched is None) or (matched is False)) - if negated: - result = not result - if result: - print("=" * 80) - print("PRUNING MUTANT DUE TO RULE:", ctype + ":", c) - utils.show(m) - pruned.append(mfile) - prunedYet = True - break - if prunedYet: - break - print("=" * 80) - print("PRUNED", len(pruned), "MUTANTS") - with open(outfile, 'w') as newmfile: - for m in mutants: - if m[0] not in pruned: - newmfile.write(m[0] + "\n") - - -if __name__ == '__main__': - main() diff --git a/build/lib/universalmutator/python_handler.py b/build/lib/universalmutator/python_handler.py deleted file mode 100644 index fedb8db..0000000 --- a/build/lib/universalmutator/python_handler.py +++ /dev/null @@ -1,85 +0,0 @@ -import marshal -import os -import sys -import py_compile -import uuid - - -def buildCode(c): - val = [] - try: - val.append(c.co_code) - except BaseException: - if isinstance(c, str): - val.append("@STRING@:" + c) - else: - val.append(c) - try: - for cc in c.co_consts: - bc = buildCode(cc) - try: - if bc[0].find("@STRING@") == 0: - bc = bc[1:] - except BaseException: - pass - val.append(bc) - except BaseException: - pass - return tuple(val) - - -def getPythonCode(fname): - # from https://stackoverflow.com/questions/32562163/how-can-i-understand-a-pyc-file-content - header_sizes = [ - # (size, first version this applies to) - # pyc files were introduced in 0.9.2 way, way back in June 1991. - (8, (0, 9, 2)), # 2 bytes magic number, \r\n, 4 bytes UNIX timestamp - (12, (3, 6)), # added 4 bytes file size - # bytes 4-8 are flags, meaning of 9-16 depends on what flags are set - # bit 0 not set: 9-12 timestamp, 13-16 file size - # bit 0 set: 9-16 file hash (SipHash-2-4, k0 = 4 bytes of the file, k1 = 0) - (16, (3, 7)), # inserted 4 bytes bit flag field at 4-8 - # future version may add more bytes still, at which point we can extend - # this table. It is correct for Python versions up to 3.9 - ] - header_size = next(s for s, v in reversed(header_sizes) if sys.version_info >= v) - - with open(fname, "rb") as f: - _ = f.read(header_size) # first header_size bytes are metadata - try: - code = marshal.load(f) # rest is a marshalled code object - except BaseException: - print("WARNING: UNABLE TO MARSHAL CODE FROM PYC FILE!") - return uuid.uuid4() - if "code" not in str(type(code)): - print("WARNING: INVALID CODE OBJECT READ FROM PYC FILE!") - return uuid.uuid4() - b = buildCode(code) - return b - - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - compiled = tmpMutantName.replace(".py", ".pyc") - - if os.path.exists(compiled): - os.remove(compiled) - - try: - py_compile.compile(tmpMutantName, doraise=True, cfile=compiled) - except py_compile.PyCompileError: - return "INVALID" - - if len(uniqueMutants) == 0: - sourceCompiled = sourceFile.replace(".py", ".pyc") - py_compile.compile(sourceFile, cfile=sourceCompiled) - if os.path.exists(sourceCompiled): - uniqueMutants[getPythonCode(sourceCompiled)] = 1 - - if os.path.exists(compiled): - code = getPythonCode(compiled) - if code in uniqueMutants: - uniqueMutants[code] += 1 - return "REDUNDANT" - uniqueMutants[code] = 1 - return "VALID" - return "INVALID" diff --git a/build/lib/universalmutator/r_handler.py b/build/lib/universalmutator/r_handler.py deleted file mode 100644 index 2222de5..0000000 --- a/build/lib/universalmutator/r_handler.py +++ /dev/null @@ -1,4 +0,0 @@ -dumb = True - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - return "VALID" diff --git a/build/lib/universalmutator/rust_handler.py b/build/lib/universalmutator/rust_handler.py deleted file mode 100644 index 980aea6..0000000 --- a/build/lib/universalmutator/rust_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -import subprocess -import shutil - - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - backupName = tmpMutantName + ".backup." + str(os.getpid()) - outName = ".um.mutant_output." + str(os.getpid()) - if len(uniqueMutants) == 0: - shutil.copy(tmpMutantName, backupName) - shutil.copy(sourceFile, tmpMutantName) - with open(outName, 'w') as file: - r = subprocess.call(["rustc", tmpMutantName], - stdout=file, stderr=file) - with open(tmpMutantName.replace(".rs", ""), 'rb') as file: - uniqueMutants[file.read()] = 1 - shutil.copy(backupName, tmpMutantName) - with open(outName, 'w') as file: - r = subprocess.call(["rustc", tmpMutantName], stdout=file, stderr=file) - if r == 0: - with open(tmpMutantName.replace(".rs", ""), 'rb') as file: - code = file.read() - if code in uniqueMutants: - uniqueMutants[code] += 1 - return "REDUNDANT" - uniqueMutants[code] = 1 - return "VALID" - return "INVALID" diff --git a/build/lib/universalmutator/show.py b/build/lib/universalmutator/show.py deleted file mode 100644 index b63bd68..0000000 --- a/build/lib/universalmutator/show.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import print_function -import sys - -from universalmutator import utils - - -def main(): - - args = sys.argv - - if ("--help" in args) or (len(sys.argv) < 2): - if len(sys.argv) < 2: - print("ERROR: show_mutants requires at least one argument\n") - print("USAGE: show_mutants [--mutantDir ] [--sourceDir ]") - print(" --mutantDir: directory with all mutants; defaults to current directory") - print(" --sourceDir: directory of source files; defaults to current directory") - print(" --concise: display in concise mutant format") - sys.exit(0) - - infile = sys.argv[1] - - concise = "--concise" in sys.argv - if concise: - args.remove("--concise") - - mdir = "." - try: - mdirpos = args.index("--mutantDir") - except ValueError: - mdirpos = -1 - - if mdirpos != -1: - mdir = args[mdirpos + 1] - args.remove("--mutantDir") - args.remove(mdir) - if mdir[-1] != "/": - mdir += "/" - - sdir = "." - try: - sdirpos = args.index("--sourceDir") - except ValueError: - sdirpos = -1 - - if sdirpos != -1: - sdir = args[sdirpos + 1] - args.remove("--sourceDir") - args.remove(sdir) - if sdir[-1] != "/": - sdir += "/" - - mutants = [] - with open(infile, 'r') as mfile: - for line in mfile: - name = line[:-1] - suffix = "." + name.split(".")[-1] - mpart = ".mutant." + name.split(".mutant.")[1] - original = sdir + name.replace(mpart, suffix) - mutants.append(utils.readMutant(name, original, mutantDir=mdir)) - print("READ", len(mutants), "MUTANTS") - - pos = 1 - for m in mutants: - print("*"*80) - print("MUTANT #" + str(pos) + ":") - pos += 1 - utils.show(m, concise=concise, mutantDir=mdir) - - -if __name__ == '__main__': - main() diff --git a/build/lib/universalmutator/solidity_handler.py b/build/lib/universalmutator/solidity_handler.py deleted file mode 100644 index 766b94f..0000000 --- a/build/lib/universalmutator/solidity_handler.py +++ /dev/null @@ -1,59 +0,0 @@ -import os -import subprocess -import shutil - - -def extractASM(text, filename): - newText = "" - lines = text.split("\n") - assemblyStart = False - for l in lines: - if assemblyStart: - if (filename not in l) and ("auxdata: 0x" not in l): - newText += (l + "\n") - elif l.find("EVM assembly:") == 0: - assemblyStart = True - return newText - - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants, compileFile=None): - copyForImport = False - if compileFile is None: - compileFile = tmpMutantName - else: - shutil.copy(sourceFile, ".um.out." + str(os.getpid()) + ".src_backup") - copyForImport = True - outName = ".um.out." + str(os.getpid()) + ".asm" - if len(uniqueMutants) == 0: - shutil.copy(tmpMutantName, tmpMutantName + ".backup." + str(os.getpid())) - shutil.copy(sourceFile, tmpMutantName) - with open(outName, 'w') as file: - r = subprocess.call( - ["solc", compileFile, "--asm", "--optimize"], stdout=file, stderr=file) - with open(outName, 'r') as file: - if not copyForImport: - uniqueMutants[extractASM(file.read(), tmpMutantName)] = 1 - else: - uniqueMutants[extractASM(file.read(), sourceFile)] = 1 - shutil.copy(tmpMutantName + ".backup." + str(os.getpid()), tmpMutantName) - if copyForImport: - shutil.copy(tmpMutantName, sourceFile) - try: - with open(outName, 'w') as file: - r = subprocess.call(["solc", compileFile, "--asm", - "--optimize"], stdout=file, stderr=file) - finally: - if copyForImport: - shutil.copy(".um.out." + str(os.getpid()) + ".src_backup", sourceFile) - if r == 0: - with open(outName, 'r') as file: - if not copyForImport: - code = extractASM(file.read(), tmpMutantName) - else: - code = extractASM(file.read(), sourceFile) - if code in uniqueMutants: - uniqueMutants[code] += 1 - return "REDUNDANT" - uniqueMutants[code] = 1 - return "VALID" - return "INVALID" diff --git a/build/lib/universalmutator/static/c.rules b/build/lib/universalmutator/static/c.rules deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/universalmutator/static/c_like.rules b/build/lib/universalmutator/static/c_like.rules deleted file mode 100644 index a6a989d..0000000 --- a/build/lib/universalmutator/static/c_like.rules +++ /dev/null @@ -1,20 +0,0 @@ -#include ==> DO_NOT_MUTATE -(^\s*)(\S+[^{}]+.*)\n ==> \1/*\2*/\n -if (\(.*\)) ==> if (!\1) -if(\(.*\)) ==> if (!\1) -if (\(.*\)) ==> if (0==1) -if(\(.*\)) ==> if(0==1) -if (\(.*\)) ==> if (1==1) -if(\(.*\)) ==> if(1==1) -while (\(.*\)) ==> while (!\1) -while(\(.*\)) ==> while(!\1) -else ==> -\|\|.*\) ==> || (0==1)) -\|\|.*\s ==> || (0==1) -\(.*\|\| ==> ((0==1) || -\s.*\|\| ==> (0==1) || -&&.*\) ==> && (1==1)) -&&.*\s ==> && (1==1) -\(.*&& ==> ((1==1) && -\s.*&& ==> (1==1) && -// ==> SKIP_MUTATING_REST \ No newline at end of file diff --git a/build/lib/universalmutator/static/cpp.rules b/build/lib/universalmutator/static/cpp.rules deleted file mode 100644 index 73f0c60..0000000 --- a/build/lib/universalmutator/static/cpp.rules +++ /dev/null @@ -1,14 +0,0 @@ -any_of ==> all_of -any_of ==> none_of -all_of ==> none_of -all_of ==> any_of -none_of ==> any_of -none_of ==> all_of -at(\(.*\)) ==> at(0) -at (\(.*\)) ==> at (0) -front_inserter ==> back_inserter -back_inserter ==> front_inserter -min_element ==> max_element -max_element ==> min_element -accumulate ==> inner_product -inner_product ==> accumulate \ No newline at end of file diff --git a/build/lib/universalmutator/static/fe.rules b/build/lib/universalmutator/static/fe.rules deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/universalmutator/static/fortran.rules b/build/lib/universalmutator/static/fortran.rules deleted file mode 100644 index d5eba99..0000000 --- a/build/lib/universalmutator/static/fortran.rules +++ /dev/null @@ -1,112 +0,0 @@ -\*\* ==> + -\*\* ==> - -\*\* ==> / - -\+ ==> ** -- ==> ** -\* ==> ** -([^\*/])/([^\*/]) ==> \1**\2 - -\.lt\. ==> .eq. -\.lt\. ==> .ne. -\.lt\. ==> .le. -\.lt\. ==> .gt. -\.lt\. ==> .ge. - -\.le\. ==> .eq. -\.le\. ==> .ne. -\.le\. ==> .lt. -\.le\. ==> .gt. -\.le\. ==> .ge. - -\.gt\. ==> .eq. -\.gt\. ==> .ne. -\.gt\. ==> .le. -\.gt\. ==> .lt. -\.gt\. ==> .ge. - -\.ge\. ==> .eq. -\.ge\. ==> .ne. -\.ge\. ==> .le. -\.ge\. ==> .gt. -\.ge\. ==> .lt. - -\.eq\. ==> .ge. -\.ge\. ==> .ne. -\.ge\. ==> .le. -\.ge\. ==> .gt. -\.ge\. ==> .lt. - -\.ne\. ==> .ge. -\.ne\. ==> .eq. -\.ne\. ==> .le. -\.ne\. ==> .gt. -\.ne\. ==> .lt. - -\.and\. ==> .or. -\.and\. ==> .eqv. -\.and\. ==> .neqv. - -\.or\. ==> .and. -\.or\. ==> .eqv. -\.or\. ==> .neqv. - -\.eqv\. ==> .or. -\.eqv\. ==> .and. -\.eqv\. ==> .neqv. - -\.neqv\. ==> .or. -\.neqv\. ==> .and. -\.neqv\. ==> .eqv - -\.LT\. ==> .EQ. -\.LT\. ==> .NE. -\.LT\. ==> .LE. -\.LT\. ==> .GT. -\.LT\. ==> .GE. - -\.LE\. ==> .EQ. -\.LE\. ==> .NE. -\.LE\. ==> .LT. -\.LE\. ==> .GT. -\.LE\. ==> .GE. - -\.GT\. ==> .EQ. -\.GT\. ==> .NE. -\.GT\. ==> .LE. -\.GT\. ==> .LT. -\.GT\. ==> .GE. - -\.GE\. ==> .EQ. -\.GE\. ==> .NE. -\.GE\. ==> .LE. -\.GE\. ==> .GT. -\.GE\. ==> .LT. - -\.EQ\. ==> .GE. -\.GE\. ==> .NE. -\.GE\. ==> .LE. -\.GE\. ==> .GT. -\.GE\. ==> .LT. - -\.NE\. ==> .GE. -\.NE\. ==> .EQ. -\.NE\. ==> .LE. -\.NE\. ==> .GT. -\.NE\. ==> .LT. - -\.AND\. ==> .OR. -\.AND\. ==> .EQV. -\.AND\. ==> .NEQV. - -\.OR\. ==> .AND. -\.OR\. ==> .EQV. -\.OR\. ==> .NEQV. - -\.EQV\. ==> .OR. -\.EQV\. ==> .AND. -\.EQV\. ==> .NEQV. - -\.NEQV\. ==> .OR. -\.NEQV\. ==> .AND. -\.NEQV\. ==> .EQV \ No newline at end of file diff --git a/build/lib/universalmutator/static/go.rules b/build/lib/universalmutator/static/go.rules deleted file mode 100644 index 2c5afe2..0000000 --- a/build/lib/universalmutator/static/go.rules +++ /dev/null @@ -1,6 +0,0 @@ -&& .* ==> && true -\|\| .* ==> || false -.* && ==> true && -.* \|\| ==> false || - -defer ==> \ No newline at end of file diff --git a/build/lib/universalmutator/static/java.rules b/build/lib/universalmutator/static/java.rules deleted file mode 100644 index 5fba30f..0000000 --- a/build/lib/universalmutator/static/java.rules +++ /dev/null @@ -1,12 +0,0 @@ -(^\s*import) ==> DO_NOT_MUTATE -(^\s*(@.+)) ==> DO_NOT_MUTATE -(^\s*(\/\*.+)) ==> DO_NOT_MUTATE -(^\s*(\/\/.+)) ==> DO_NOT_MUTATE -(^\s*(\*.+)) ==> DO_NOT_MUTATE - -synchronized ==> - -&& .* ==> && true -\|\| .* ==> || false -.* && ==> true && -.* \|\| ==> false || diff --git a/build/lib/universalmutator/static/javascript.rules b/build/lib/universalmutator/static/javascript.rules deleted file mode 100644 index 124b8f7..0000000 --- a/build/lib/universalmutator/static/javascript.rules +++ /dev/null @@ -1,6 +0,0 @@ -&& .* ==> && true -\|\| .* ==> || false -.* && ==> true && -.* \|\| ==> false || - -=== ==> !== \ No newline at end of file diff --git a/build/lib/universalmutator/static/lisp.rules b/build/lib/universalmutator/static/lisp.rules deleted file mode 100644 index 33b1038..0000000 --- a/build/lib/universalmutator/static/lisp.rules +++ /dev/null @@ -1,7 +0,0 @@ -and ==> or -or ==> and -not ==> -or ==> or T -and ==> and nil -T ==> nil -nil ==> T diff --git a/build/lib/universalmutator/static/none.rules b/build/lib/universalmutator/static/none.rules deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/universalmutator/static/python.rules b/build/lib/universalmutator/static/python.rules deleted file mode 100644 index d4cce05..0000000 --- a/build/lib/universalmutator/static/python.rules +++ /dev/null @@ -1,43 +0,0 @@ -(^\s*import) ==> DO_NOT_MUTATE - -if (.*):\n ==> if not (\1):\n -while (.*):\n ==> while not (\1):\n - -continue ==> break -break ==> continue - -if \( ==> if not ( - -while \( ==> while not ( - -and ==> or -or ==> and -and .* ==> and True -or .* ==> or False -.* and ==> True and -.* or ==> False or - -not ==> - -return .*\n ==> return None\n - -(^\s*)(\S+.*\n) ==> \1pass - -// ==> / -/ ==> // - -True ==> False - -\[.+\] ==> [] -\[.*, ==> [ -,.*] ==> ] -{.+} ==> {} -{.*, ==> { -,.*} ==> } - -,.+, ==> , -'.+' ==> '' - -@.* ==> - -# ==> SKIP_MUTATING_REST \ No newline at end of file diff --git a/build/lib/universalmutator/static/r.rules b/build/lib/universalmutator/static/r.rules deleted file mode 100644 index 1c69a9e..0000000 --- a/build/lib/universalmutator/static/r.rules +++ /dev/null @@ -1 +0,0 @@ -# ==> SKIP_MUTATING_REST \ No newline at end of file diff --git a/build/lib/universalmutator/static/rust.rules b/build/lib/universalmutator/static/rust.rules deleted file mode 100644 index 26b8bd2..0000000 --- a/build/lib/universalmutator/static/rust.rules +++ /dev/null @@ -1,2 +0,0 @@ -true ==> false -false ==> true diff --git a/build/lib/universalmutator/static/solidity.rules b/build/lib/universalmutator/static/solidity.rules deleted file mode 100644 index 88e5541..0000000 --- a/build/lib/universalmutator/static/solidity.rules +++ /dev/null @@ -1,50 +0,0 @@ -pragma solidity ==> DO_NOT_MUTATE -^import\s ==> DO_NOT_MUTATE -true ==> false -false ==> true -([^u])int ==> \1uint -uint ==> int -([^u])fixed ==> \1ufixed -ufixed ==> fixed -int16 ==> int8 -int32 ==> int16 -int64 ==> int32 -memory ==> storage -storage ==> memory -view ==> pure -pure ==> view -constant ==> pure -payable ==> -private ==> -msg.sender ==> tx.origin -tx.origin ==> msg.sender -ether ==> wei -wei|finney|szabo ==> ether -ether|finney|szabo ==> wei -wei|ether|szao ==> finney -wei|finney|ether ==> szabo -minutes|days|hours|weeks|years ==> seconds -seconds|days|hours|weeks|years ==> minutes -seconds|minutes|hours|weeks|years ==> days -seconds|minutes|days|weeks|years ==> hours -seconds|minutes|days|hours|years ==> weeks -seconds|minutes|days|hours|weeks ==> years -now ==> 0 -block.timestamp ==> 0 -msg.value ==> 0 -msg.value ==> 1 -addmod ==> mulmod -mulmod ==> addmod -(^\s*)(\S+[^{}]+.*)\n ==> selfdestruct(msg.sender); -(^\s*)(\S+[^{}]+.*)\n ==> revert(); -call ==> delegatecall -delegatecall ==> call -call ==> callcode -callcode ==> call -delegatecall ==> callcode -callcode ==> delegatecall -(\S+\s+)(\S+)(\s+) ==> \1\3 -if (\(.*\)) ==> if (false) -if(\(.*\)) ==> if(false) -if (\(.*\)) ==> if (true) -if(\(.*\)) ==> if(true) diff --git a/build/lib/universalmutator/static/swift.rules b/build/lib/universalmutator/static/swift.rules deleted file mode 100644 index 800aba3..0000000 --- a/build/lib/universalmutator/static/swift.rules +++ /dev/null @@ -1,15 +0,0 @@ -var ==> let - -true ==> false -false ==> true - -\[.+] ==> [] -\[.*, ==> [ -,.*] ==> ] - -,.+, ==> , - -&& .* ==> && true -\|\| .* ==> || false -.* && ==> true && -.* \|\| ==> false || diff --git a/build/lib/universalmutator/static/universal.rules b/build/lib/universalmutator/static/universal.rules deleted file mode 100644 index e3b6d78..0000000 --- a/build/lib/universalmutator/static/universal.rules +++ /dev/null @@ -1,96 +0,0 @@ -DO_NOT_MUTATE ==> DO_NOT_MUTATE - -\+ ==> - -\+ ==> * -\+ ==> / -\+ ==> % - --([^>]) ==> +\1 --([^>]) ==> *\1 --([^>]) ==> /\1 --([^>]) ==> %\1 - -\* ==> + -\* ==> - -\* ==> / -\* ==> % - -([^\*/])/([^\*/]) ==> \1+\2 -([^\*/])/([^\*/]) ==> \1-\2 -([^\*/])/([^\*/]) ==> \1*\2 -([^\*/])/([^\*/]) ==> \1%\2 - -% ==> + -% ==> - -% ==> * -% ==> / - -!= ==> == -!= ==> <= -!= ==> >= -!= ==> > -!= ==> < - -== ==> != -== ==> <= -== ==> >= -== ==> > -== ==> < - ->= ==> == ->= ==> != ->= ==> < ->= ==> > - -<= ==> == -<= ==> != -<= ==> < -<= ==> > - -< ==> > -< ==> == -< ==> <= -< ==> >= -< ==> != - -([^-])> ==> \1< -([^-])> ==> \1== -([^-])> ==> \1>= -([^-])> ==> \1<= -([^-])> ==> \1!= - -\+= ==> =+ --= ==> =- - --- ==> ++ -\+\+ ==> -- - --([^>]) ==> \1 - -(\D)(\d+)(\D) ==> \g<1>0\3 -(\D)(\d+)(\D) ==> \g<1>1\3 -(\D)(\d+)(\D) ==> \g<1>-1\3 -(\D)(\d+)(\D) ==> \1(\2+1)\3 -(\D)(\d+)(\D) ==> \1(\2-1)\3 - -&& ==> || -\|\| ==> && -! ==> - -([^&])&([^&]) ==> \1|\2 -([^|])\|([^|]) ==> \1&\2 - -(^\s*)(\S+.*)\n ==> \1\2\n\1break;\n -(^\s*)(\S+.*)\n ==> \1\2\n\1continue;\n - -".+" ==> "" - -while ==> if - -([,\(\[\{])([^=,\)\}\]]+),([^=,\)\}\]]+)([,\)\]\}]) ==> \1\3,\2\4 -([,\(\[\{])([^,=\)\]\}]+)=([^,=\)\]\}]+),([^,=\)\]\}]+)=([^,=\)\]\}]+)([,\)\]\}]) ==> \1\4=\3,\2=\5\6 - -min ==> max -max ==> min -begin ==> end -end ==> begin diff --git a/build/lib/universalmutator/static/vyper.rules b/build/lib/universalmutator/static/vyper.rules deleted file mode 100644 index e69de29..0000000 diff --git a/build/lib/universalmutator/swift_handler.py b/build/lib/universalmutator/swift_handler.py deleted file mode 100644 index 956432d..0000000 --- a/build/lib/universalmutator/swift_handler.py +++ /dev/null @@ -1,29 +0,0 @@ -import os -import subprocess -import shutil - - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - backupName = tmpMutantName + ".backup." + str(os.getpid()) - outName = ".um.mutant.output." + str(os.getpid()) - if len(uniqueMutants) == 0: - shutil.copy(tmpMutantName, backupName) - shutil.copy(sourceFile, tmpMutantName) - with open(outName, 'w') as file: - r = subprocess.call(["swiftc", tmpMutantName], - stdout=file, stderr=file) - with open(tmpMutantName.replace(".swift", ""), 'rb') as file: - uniqueMutants[file.read()] = 1 - shutil.copy(backupName, tmpMutantName) - with open(outName, 'w') as file: - r = subprocess.call(["swiftc", tmpMutantName], - stdout=file, stderr=file) - if r == 0: - with open(tmpMutantName.replace(".swift", ""), 'rb') as file: - code = file.read() - if code in uniqueMutants: - uniqueMutants[code] += 1 - return "REDUNDANT" - uniqueMutants[code] = 1 - return "VALID" - return "INVALID" diff --git a/build/lib/universalmutator/utils.py b/build/lib/universalmutator/utils.py deleted file mode 100644 index d8f0c78..0000000 --- a/build/lib/universalmutator/utils.py +++ /dev/null @@ -1,221 +0,0 @@ -from __future__ import print_function -import difflib -import time - -import Levenshtein - - -def solidityContract(m): - (_, sourcefile, pos, _, _) = m - cname = "UNKNOWN" - try: - with open(sourcefile, 'r') as readm: - cpos = 0 - for line in readm: - if ("contract " in line) and (line.split()[0][:2] != "//"): - cname = line.split("contract ")[1] - if ("library " in line) and (line.split()[0][:2] != "//"): - cname = "library:" + line.split("library ")[1] - cpos += 1 - if cpos > pos: - break - except KeyboardInterrupt: - raise - except BaseException: - pass - actualName = "" - for c in cname: - if c.isspace() or (c == "("): - break - actualName += c - return actualName - - -def solidityFunction(m): - (_, sourcefile, pos, _, _) = m - fname = "UNKNOWN" - try: - with open(sourcefile, 'r') as readm: - fpos = 0 - for line in readm: - if ("function " in line) and (line.split()[0][:2] != "//"): - fname = line.split("function ")[1] - fpos += 1 - if fpos > pos: - break - except KeyboardInterrupt: - raise - except BaseException: - pass - actualName = "" - for c in fname: - if c.isspace() or (c == "("): - break - actualName += c - return actualName - - -def show(m, concise=False, mutantDir=None, sourceDir=None): - (mfile, sourcefile, pos, orig, mutant) = m - print(mfile + ": " + sourcefile + ":" + str(pos + 1)) - if sourcefile.split(".")[1] == "sol": - print("Function", solidityFunction(m), "in contract", solidityContract(m)) - if concise: - print(orig, end="") - print(" ==> ", change(m)) - print(mutant, end="") - else: - if mutantDir is not None: - mfile = mutantDir + "/" + mfile - if sourceDir is not None: - sourcefile = sourceDir + "/" + sourcefile - with open(sourcefile, 'r') as ff: - fromLines = ff.readlines() - with open(mfile, 'r') as tf: - toLines = tf.readlines() - diff = difflib.context_diff(fromLines, toLines, "Original", "Mutant") - print(''.join(diff)) - print() - - -def change(m): - (_, _, pos, orig, mutant) = m - eops = Levenshtein.editops(orig, mutant) - blocks = Levenshtein.matching_blocks(eops, orig, mutant) - if len(blocks) > 4: - return mutant[:-1] - keep = ''.join([orig[x[0]:x[0]+x[2]] for x in blocks]) - notKeep = "" - pos = 0 - wasDot = False - for c in range(0, len(orig)): - if orig[c] == keep[pos]: - pos += 1 - if not wasDot: - notKeep += "..." - wasDot = True - else: - notKeep += orig[c] - wasDot = False - notKeep += "==>" - pos = 0 - wasDot = False - for c in range(0, len(mutant)): - if (pos < len(keep)) and mutant[c] == keep[pos]: - pos += 1 - if not wasDot: - notKeep += "..." - wasDot = True - else: - notKeep += mutant[c] - wasDot = False - return notKeep - - -def isStatementDeletion(m): - return change(m) == "...==>.../*...*/..." - - -mdistanceCache = {} - - -def d(m1, m2, changeWeight=5.0, origWeight=0.1, mutantWeight=0.1, codeWeight=0.5, useCache=True): - if m1 == m2: - return 0 - if useCache: - if (m1, m2) in mdistanceCache: - return mdistanceCache[(m1, m2)] - if (m2, m1) in mdistanceCache: - return mdistanceCache[(m2, m1)] - (_, sourcefile1, pos1, orig1, mutant1) = m1 - (_, sourcefile2, pos2, orig2, mutant2) = m2 - md = changeWeight * (1.0 - (Levenshtein.ratio(change(m1), change(m2)))) - md += origWeight * (1.0 - Levenshtein.ratio(orig1, orig2)) - md += mutantWeight * (1.0 - Levenshtein.ratio(mutant1, mutant2)) - if sourcefile1 != sourcefile2: - md += codeWeight - else: - pd = abs(pos1 - pos2) - if pd > 10: - md += codeWeight * 0.5 - else: - md += codeWeight * (0.5 * (pd / 11.0)) - if useCache: - mdistanceCache[(m1, m2)] = md - return md - - -def FPF(mlist, N, f=None, df=d, cutoff=0.0, verbose=True, avoid=None, mutantDir=None, sourceDir=None): - if avoid is None: - avoid = [] - if len(mlist) == 0: - return mlist - start = time.time() - if f is None: - ranking = [(mlist[0], -1)] - else: - maxf = 0 - best = None - for m in mlist: - fm = f(m) - if fm > maxf: - best = m - maxf = fm - ranking = [(best, -1)] - if verbose: - print("*"*80) - show(ranking[0][0], mutantDir=mutantDir, sourceDir=sourceDir) - while (len(ranking) < N) and (len(ranking) < len(mlist)): - best = None - maxMin = -1 - for m1 in mlist: - dmin = -1 - for (m2, _) in ranking: - dm1m2 = df(m1, m2) - if (dm1m2 < dmin) or (dmin == -1): - dmin = dm1m2 - for m2 in avoid: - dm1m2 = df(m1, m2) - if (dm1m2 < dmin) or (dmin == -1): - dmin = dm1m2 - if dmin > maxMin: - best = m1 - maxMin = dmin - if verbose: - print("*"*80) - elapsed = time.time() - start - print("RANKED", len(ranking) + 1, "MUTANTS IN", elapsed, "SECONDS") - show(best, mutantDir=mutantDir, sourceDir=sourceDir) - print("DISTANCE:", maxMin) - ranking.append((best, maxMin)) - if maxMin < cutoff: - break - return ranking - - -def readMutant(mutant, source, mutantDir=None, sourceDir=None): - mfile = mutant - if mutantDir is not None: - mfile = mutantDir + "/" + mfile - sfile = source - if sourceDir is not None: - sfile = sourceDir + "/" + sfile - with open(sfile, 'r') as readSource: - scode = readSource.readlines() - with open(mfile, 'r') as readmfile: - mcode = readmfile.readlines() - pos = 0 - # We expect one location of change, contiguous - diffFound = False - for line in scode: - if line != mcode[pos]: - diffFound = True - break - pos += 1 - mpos = pos - if not diffFound: - if len(mcode) > len(scode): - pos = len(scode)-1 - diffFound = True - assert diffFound, "mutant " + mfile + " and source " + source + " are identical!" - return (mutant, source, pos, scode[pos], mcode[mpos]) diff --git a/build/lib/universalmutator/vyper_handler.py b/build/lib/universalmutator/vyper_handler.py deleted file mode 100644 index 024b035..0000000 --- a/build/lib/universalmutator/vyper_handler.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import subprocess -import shutil - - -def extractOpcodes(text, filename): - return text - - -def handler(tmpMutantName, mutant, sourceFile, uniqueMutants): - outName = ".um.out." + str(os.getpid()) + ".opcodes" - if len(uniqueMutants) == 0: - shutil.copy(tmpMutantName, tmpMutantName + ".backup." + str(os.getpid())) - shutil.copy(sourceFile, tmpMutantName) - with open(outName, 'w') as file: - r = subprocess.call( - ["vyper", tmpMutantName, "-f", "opcodes"], stdout=file, stderr=file) - with open(outName, 'r') as file: - uniqueMutants[extractOpcodes(file.read(), tmpMutantName)] = 1 - with open(outName, 'w') as file: - r = subprocess.call(["vyper", tmpMutantName, "-f", - "opcodes"], stdout=file, stderr=file) - if r == 0: - with open(outName, 'r') as file: - code = extractOpcodes(file.read(), tmpMutantName) - if code in uniqueMutants: - uniqueMutants[code] += 1 - return "REDUNDANT" - uniqueMutants[code] = 1 - return "VALID" - return "INVALID"