gh-122943: Move code generation for var-positional parameter to converters (GH-126575)

This commit is contained in:
Serhiy Storchaka 2024-11-08 14:12:15 +02:00 committed by GitHub
parent 403410fa1b
commit ee0746af7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 91 additions and 73 deletions

View File

@ -1232,7 +1232,18 @@ class self_converter(CConverter):
# Converters for var-positional parameter. # Converters for var-positional parameter.
class varpos_tuple_converter(CConverter): class VarPosCConverter(CConverter):
format_unit = ''
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
raise AssertionError('should never be called')
def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int,
fastcall: bool, limited_capi: bool) -> str:
raise NotImplementedError
class varpos_tuple_converter(VarPosCConverter):
type = 'PyObject *' type = 'PyObject *'
format_unit = '' format_unit = ''
c_default = 'NULL' c_default = 'NULL'
@ -1240,14 +1251,76 @@ class varpos_tuple_converter(CConverter):
def cleanup(self) -> str: def cleanup(self) -> str:
return f"""Py_XDECREF({self.parser_name});\n""" return f"""Py_XDECREF({self.parser_name});\n"""
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int,
raise AssertionError('should never be called') fastcall: bool, limited_capi: bool) -> str:
paramname = self.parser_name
if fastcall:
if limited_capi:
if min(pos_only, min_pos) < max_pos:
size = f'Py_MAX(nargs - {max_pos}, 0)'
else:
size = f'nargs - {max_pos}' if max_pos else 'nargs'
return f"""
{paramname} = PyTuple_New({size});
if (!{paramname}) {{{{
goto exit;
}}}}
for (Py_ssize_t i = {max_pos}; i < nargs; ++i) {{{{
PyTuple_SET_ITEM({paramname}, i - {max_pos}, Py_NewRef(args[i]));
}}}}
"""
else:
self.add_include('pycore_tuple.h', '_PyTuple_FromArray()')
start = f'args + {max_pos}' if max_pos else 'args'
size = f'nargs - {max_pos}' if max_pos else 'nargs'
if min(pos_only, min_pos) < max_pos:
return f"""
{paramname} = nargs > {max_pos}
? _PyTuple_FromArray({start}, {size})
: PyTuple_New(0);
if ({paramname} == NULL) {{{{
goto exit;
}}}}
"""
else:
return f"""
{paramname} = _PyTuple_FromArray({start}, {size});
if ({paramname} == NULL) {{{{
goto exit;
}}}}
"""
else:
if max_pos:
return f"""
{paramname} = PyTuple_GetSlice(args, {max_pos}, PY_SSIZE_T_MAX);
if (!{paramname}) {{{{
goto exit;
}}}}
"""
else:
return f"{paramname} = Py_NewRef(args);\n"
class varpos_array_converter(CConverter):
class varpos_array_converter(VarPosCConverter):
type = 'PyObject * const *' type = 'PyObject * const *'
format_unit = ''
length = True length = True
c_ignored_default = '' c_ignored_default = ''
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int,
raise AssertionError('should never be called') fastcall: bool, limited_capi: bool) -> str:
paramname = self.parser_name
if not fastcall:
self.add_include('pycore_tuple.h', '_PyTuple_ITEMS()')
start = 'args' if fastcall else '_PyTuple_ITEMS(args)'
size = 'nargs' if fastcall else 'PyTuple_GET_SIZE(args)'
if max_pos:
if min(pos_only, min_pos) < max_pos:
start = f'{size} > {max_pos} ? {start} + {max_pos} : {start}'
size = f'Py_MAX(0, {size} - {max_pos})'
else:
start = f'{start} + {max_pos}'
size = f'{size} - {max_pos}'
return f"""
{paramname} = {start};
{self.length_name} = {size};
"""

View File

@ -452,71 +452,13 @@ class ParseArgsCodeGen:
def _parse_vararg(self) -> str: def _parse_vararg(self) -> str:
assert self.varpos is not None assert self.varpos is not None
paramname = self.varpos.converter.parser_name c = self.varpos.converter
if self.varpos.converter.length: assert isinstance(c, libclinic.converters.VarPosCConverter)
if not self.fastcall: return c.parse_vararg(pos_only=self.pos_only,
self.codegen.add_include('pycore_tuple.h', min_pos=self.min_pos,
'_PyTuple_ITEMS()') max_pos=self.max_pos,
start = 'args' if self.fastcall else '_PyTuple_ITEMS(args)' fastcall=self.fastcall,
size = 'nargs' if self.fastcall else 'PyTuple_GET_SIZE(args)' limited_capi=self.limited_capi)
if self.max_pos:
if min(self.pos_only, self.min_pos) < self.max_pos:
start = f'{size} > {self.max_pos} ? {start} + {self.max_pos} : {start}'
size = f'Py_MAX(0, {size} - {self.max_pos})'
else:
start = f'{start} + {self.max_pos}'
size = f'{size} - {self.max_pos}'
return f"""
{paramname} = {start};
{self.varpos.converter.length_name} = {size};
"""
if self.fastcall:
if self.limited_capi:
if min(self.pos_only, self.min_pos) < self.max_pos:
size = f'Py_MAX(nargs - {self.max_pos}, 0)'
else:
size = f'nargs - {self.max_pos}' if self.max_pos else 'nargs'
return f"""
{paramname} = PyTuple_New({size});
if (!{paramname}) {{{{
goto exit;
}}}}
for (Py_ssize_t i = {self.max_pos}; i < nargs; ++i) {{{{
PyTuple_SET_ITEM({paramname}, i - {self.max_pos}, Py_NewRef(args[i]));
}}}}
"""
else:
self.codegen.add_include('pycore_tuple.h',
'_PyTuple_FromArray()')
if min(self.pos_only, self.min_pos) < self.max_pos:
return f"""
{paramname} = nargs > {self.max_pos}
? _PyTuple_FromArray(args + {self.max_pos}, nargs - {self.max_pos})
: PyTuple_New(0);
if ({paramname} == NULL) {{{{
goto exit;
}}}}
"""
else:
start = f'args + {self.max_pos}' if self.max_pos else 'args'
size = f'nargs - {self.max_pos}' if self.max_pos else 'nargs'
return f"""
{paramname} = _PyTuple_FromArray({start}, {size});
if ({paramname} == NULL) {{{{
goto exit;
}}}}
"""
else:
if self.max_pos:
return f"""
{paramname} = PyTuple_GetSlice(args, {self.max_pos}, PY_SSIZE_T_MAX);
if (!{paramname}) {{{{
goto exit;
}}}}
"""
else:
return f"{paramname} = Py_NewRef(args);\n"
def parse_pos_only(self) -> None: def parse_pos_only(self) -> None:
if self.fastcall: if self.fastcall:
@ -839,7 +781,10 @@ class ParseArgsCodeGen:
def copy_includes(self) -> None: def copy_includes(self) -> None:
# Copy includes from parameters to Clinic after parse_arg() # Copy includes from parameters to Clinic after parse_arg()
# has been called above. # has been called above.
for converter in self.converters: converters = self.converters
if self.varpos:
converters = converters + [self.varpos.converter]
for converter in converters:
for include in converter.get_includes(): for include in converter.get_includes():
self.codegen.add_include( self.codegen.add_include(
include.filename, include.filename,