@@ -1817,6 +1817,129 @@ def test_multiple_labels(self):
18171817 }
18181818 """
18191819
1820+ class TestGeneratedTailCallHandlers (unittest .TestCase ):
1821+ def setUp (self ) -> None :
1822+ super ().setUp ()
1823+ self .maxDiff = None
1824+
1825+ self .temp_dir = tempfile .gettempdir ()
1826+ self .temp_input_filename = os .path .join (self .temp_dir , "input.txt" )
1827+ self .temp_output_filename = os .path .join (self .temp_dir , "output.txt" )
1828+ self .temp_labels_output_filename = os .path .join (self .temp_dir , "labels_output.txt" )
1829+
1830+ def tearDown (self ) -> None :
1831+ for filename in [
1832+ self .temp_input_filename ,
1833+ self .temp_output_filename ,
1834+ self .temp_labels_output_filename ,
1835+ ]:
1836+ try :
1837+ os .remove (filename )
1838+ except Exception :
1839+ pass
1840+ super ().tearDown ()
1841+
1842+ def run_cases_test (self , input : str , expected : str , expected_labels : str ):
1843+ with open (self .temp_input_filename , "w+" ) as temp_input :
1844+ temp_input .write (parser .BEGIN_MARKER )
1845+ temp_input .write (input )
1846+ temp_input .write (parser .END_MARKER )
1847+ temp_input .flush ()
1848+
1849+ with handle_stderr ():
1850+ tier1_tail_call_generator .generate_tier1_from_files (
1851+ [self .temp_input_filename ], self .temp_output_filename , self .temp_labels_output_filename , False
1852+ )
1853+
1854+ with open (self .temp_output_filename ) as temp_output :
1855+ lines = temp_output .read ()
1856+ _ , rest = lines .split (tier1_generator .INSTRUCTION_START_MARKER )
1857+ instructions , _ = rest .split (tier1_generator .INSTRUCTION_END_MARKER )
1858+
1859+ with open (self .temp_labels_output_filename ) as temp_output :
1860+ lines = temp_output .readlines ()
1861+ while lines and lines [0 ].startswith (("// " , "#" , " #" , "\n " )):
1862+ lines .pop (0 )
1863+ while lines and lines [- 1 ].startswith (("#" , "\n " )):
1864+ lines .pop (- 1 )
1865+ actual_labels = "" .join (lines )
1866+
1867+ self .assertEqual (instructions .strip (), expected .strip ())
1868+ self .assertEqual (actual_labels .strip (), expected_labels .strip ())
1869+
1870+ def test_basic (self ):
1871+ input = """
1872+ inst(OP, (--)) {
1873+ SPAM();
1874+ }
1875+ """
1876+ output = """
1877+ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_OP(TAIL_CALL_PARAMS) {
1878+ {
1879+ frame->instr_ptr = next_instr;
1880+ next_instr += 1;
1881+ INSTRUCTION_STATS(OP);
1882+ SPAM();
1883+ }
1884+ DISPATCH();
1885+ }
1886+ """
1887+ output_labels = ""
1888+ self .run_cases_test (input , output , output_labels )
1889+
1890+ def test_label_transformed (self ):
1891+ """This tests that the labels and their gotos/DISPATCH get transformed to tail calls in the
1892+ tail-calling interpreter, while staying the same/becoming an entry call in the normal interpreter.
1893+ """
1894+ input = """
1895+ inst(OP, (--)) {
1896+ SPAM();
1897+ }
1898+
1899+ label(hello) {
1900+ EGGS();
1901+ if (x) {
1902+ goto baz;
1903+ }
1904+ DISPATCH();
1905+ }
1906+ """
1907+ output = """
1908+ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_hello(TAIL_CALL_PARAMS)
1909+ {
1910+ EGGS();
1911+ if (x) {
1912+ TAIL_CALL(baz);
1913+ }
1914+ DISPATCH();
1915+ }
1916+
1917+
1918+ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_OP(TAIL_CALL_PARAMS) {
1919+ {
1920+ frame->instr_ptr = next_instr;
1921+ next_instr += 1;
1922+ INSTRUCTION_STATS(OP);
1923+ SPAM();
1924+ }
1925+ DISPATCH();
1926+ hello:
1927+ TAIL_CALL(hello);
1928+ }
1929+ """
1930+ output_labels = """
1931+ hello:
1932+ {
1933+ EGGS();
1934+ if (x) {
1935+ goto baz;
1936+ }
1937+ return _TAIL_CALL_entry(frame, stack_pointer, tstate, next_instr, 0, 0);
1938+ }
1939+ """
1940+ self .run_cases_test (input , output , output_labels )
1941+
1942+
18201943class TestGeneratedAbstractCases (unittest .TestCase ):
18211944 def setUp (self ) -> None :
18221945 super ().setUp ()
0 commit comments