|
4 | 4 | import sys |
5 | 5 | import tempfile |
6 | 6 | import unittest |
| 7 | +import textwrap |
7 | 8 |
|
8 | 9 | from io import StringIO |
9 | 10 | from test import support |
@@ -36,6 +37,7 @@ def skip_if_different_mount_drives(): |
36 | 37 | import parser |
37 | 38 | from stack import Local, Stack |
38 | 39 | import tier1_generator |
| 40 | + import tier1_tail_call_generator |
39 | 41 | import opcode_metadata_generator |
40 | 42 | import optimizer_generator |
41 | 43 |
|
@@ -1714,6 +1716,156 @@ def test_pop_dead_inputs_with_output(self): |
1714 | 1716 | self.run_cases_test(input, output) |
1715 | 1717 |
|
1716 | 1718 |
|
| 1719 | +class TestGeneratedTailCallErorHandlers(unittest.TestCase): |
| 1720 | + def setUp(self) -> None: |
| 1721 | + super().setUp() |
| 1722 | + self.maxDiff = None |
| 1723 | + |
| 1724 | + self.temp_dir = tempfile.gettempdir() |
| 1725 | + self.temp_input_filename = os.path.join(self.temp_dir, "input.txt") |
| 1726 | + self.temp_output_filename = os.path.join(self.temp_dir, "output.txt") |
| 1727 | + self.temp_metadata_filename = os.path.join(self.temp_dir, "metadata.txt") |
| 1728 | + self.temp_pymetadata_filename = os.path.join(self.temp_dir, "pymetadata.txt") |
| 1729 | + self.temp_executor_filename = os.path.join(self.temp_dir, "executor.txt") |
| 1730 | + |
| 1731 | + def tearDown(self) -> None: |
| 1732 | + for filename in [ |
| 1733 | + self.temp_input_filename, |
| 1734 | + self.temp_output_filename, |
| 1735 | + self.temp_metadata_filename, |
| 1736 | + self.temp_pymetadata_filename, |
| 1737 | + self.temp_executor_filename, |
| 1738 | + ]: |
| 1739 | + try: |
| 1740 | + os.remove(filename) |
| 1741 | + except: |
| 1742 | + pass |
| 1743 | + super().tearDown() |
| 1744 | + |
| 1745 | + def run_cases_test(self, input: str, expected: str): |
| 1746 | + with open(self.temp_input_filename, "w+") as temp_input: |
| 1747 | + temp_input.write(textwrap.dedent(input)) |
| 1748 | + temp_input.flush() |
| 1749 | + |
| 1750 | + with handle_stderr(): |
| 1751 | + tier1_tail_call_generator.generate_label_handlers_from_files( |
| 1752 | + self.temp_input_filename, self.temp_output_filename |
| 1753 | + ) |
| 1754 | + |
| 1755 | + with open(self.temp_output_filename) as temp_output: |
| 1756 | + lines = temp_output.readlines() |
| 1757 | + while lines and lines[0].startswith(("// ", "#", " #", "\n")): |
| 1758 | + lines.pop(0) |
| 1759 | + while lines and lines[-1].startswith(("#", "\n")): |
| 1760 | + lines.pop(-1) |
| 1761 | + actual = "".join(lines) |
| 1762 | + |
| 1763 | + self.assertEqual(actual.strip(), textwrap.dedent(expected).strip()) |
| 1764 | + |
| 1765 | + def test_correctly_finds_pyeval_framedefault(self): |
| 1766 | + input = """ |
| 1767 | + PyObject* _Py_HOT_FUNCTION |
| 1768 | + _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) |
| 1769 | + { |
| 1770 | + |
| 1771 | + /* END_BASE_INTERPRETER */ |
| 1772 | + } |
| 1773 | + """ |
| 1774 | + output = """ |
| 1775 | + """ |
| 1776 | + self.run_cases_test(input, output) |
| 1777 | + |
| 1778 | + def test_simple_label(self): |
| 1779 | + input = """ |
| 1780 | + PyObject* _Py_HOT_FUNCTION |
| 1781 | + _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) |
| 1782 | + { |
| 1783 | + |
| 1784 | + TAIL_CALL_TARGET(error): |
| 1785 | + DO_THING(); |
| 1786 | + /* END_BASE_INTERPRETER */ |
| 1787 | + } |
| 1788 | + """ |
| 1789 | + output = """ |
| 1790 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS); |
| 1791 | +
|
| 1792 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS) |
| 1793 | + { |
| 1794 | + DO_THING(); |
| 1795 | + /* END_BASE_INTERPRETER */ |
| 1796 | + } |
| 1797 | + """ |
| 1798 | + self.run_cases_test(input, output) |
| 1799 | + |
| 1800 | + def test_fallthrough_label(self): |
| 1801 | + input = """ |
| 1802 | + PyObject* _Py_HOT_FUNCTION |
| 1803 | + _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) |
| 1804 | + { |
| 1805 | + |
| 1806 | + TAIL_CALL_TARGET(error): |
| 1807 | + DO_THING(); |
| 1808 | + TAIL_CALL_TARGET(fallthrough): |
| 1809 | + DO_THING2(); |
| 1810 | + /* END_BASE_INTERPRETER */ |
| 1811 | + } |
| 1812 | + """ |
| 1813 | + output = """ |
| 1814 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS); |
| 1815 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_fallthrough(TAIL_CALL_PARAMS); |
| 1816 | +
|
| 1817 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS) |
| 1818 | + { |
| 1819 | + DO_THING(); |
| 1820 | + CEVAL_GOTO(fallthrough); |
| 1821 | + } |
| 1822 | +
|
| 1823 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_fallthrough(TAIL_CALL_PARAMS) |
| 1824 | + { |
| 1825 | + DO_THING2(); |
| 1826 | + /* END_BASE_INTERPRETER */ |
| 1827 | + } |
| 1828 | + """ |
| 1829 | + self.run_cases_test(input, output) |
| 1830 | + |
| 1831 | + def test_transform_gotos(self): |
| 1832 | + input = """ |
| 1833 | + PyObject* _Py_HOT_FUNCTION |
| 1834 | + _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) |
| 1835 | + { |
| 1836 | + |
| 1837 | + TAIL_CALL_TARGET(error): |
| 1838 | + if (thing) { |
| 1839 | + goto fallthrough; |
| 1840 | + } |
| 1841 | + DO_THING(); |
| 1842 | + TAIL_CALL_TARGET(fallthrough): |
| 1843 | + DO_THING2(); |
| 1844 | + /* END_BASE_INTERPRETER */ |
| 1845 | + } |
| 1846 | + """ |
| 1847 | + output = """ |
| 1848 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS); |
| 1849 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_fallthrough(TAIL_CALL_PARAMS); |
| 1850 | +
|
| 1851 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_error(TAIL_CALL_PARAMS) |
| 1852 | + { |
| 1853 | + if (thing) { |
| 1854 | + CEVAL_GOTO(fallthrough); |
| 1855 | + } |
| 1856 | + DO_THING(); |
| 1857 | + CEVAL_GOTO(fallthrough); |
| 1858 | + } |
| 1859 | +
|
| 1860 | + Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_fallthrough(TAIL_CALL_PARAMS) |
| 1861 | + { |
| 1862 | + DO_THING2(); |
| 1863 | + /* END_BASE_INTERPRETER */ |
| 1864 | + } |
| 1865 | + """ |
| 1866 | + self.run_cases_test(input, output) |
| 1867 | + |
| 1868 | + |
1717 | 1869 | class TestGeneratedAbstractCases(unittest.TestCase): |
1718 | 1870 | def setUp(self) -> None: |
1719 | 1871 | super().setUp() |
|
0 commit comments