Skip to content

Commit 42d18fd

Browse files
committed
python-native: Support explicit args in fastcall format
Right now, doesn't actually expose these as fastcall yet, so it isn't efficient to use yet, but this is a good starting point for compatibility and later implementation
1 parent 94afbad commit 42d18fd

File tree

6 files changed

+144
-6
lines changed

6 files changed

+144
-6
lines changed

src/interrogate/functionRemap.cxx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -684,9 +684,15 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
684684
if (fname == "__traverse__") {
685685
// Hack to record this even though we can't wrap visitproc.
686686
param._remap = new ParameterRemapUnchanged(type);
687-
} else {
688-
// nout << "Can't handle parameter " << i << " of method " <<
689-
// *_cppfunc << "\n";
687+
}
688+
else if (param._name == "args" &&
689+
TypeManager::is_pointer_to_pointer_to_PyObject(type) &&
690+
TypeManager::is_const_pointer_to_anything(type)) {
691+
// Special case for PyObject *const *args
692+
param._remap = new ParameterRemapUnchanged(type);
693+
}
694+
else {
695+
//nout << "Can't handle parameter " << i << " of method " << *_cppfunc << "\n";
690696
return false;
691697
}
692698
} else {
@@ -824,6 +830,18 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
824830
_flags |= F_explicit_args;
825831
_args_type = InterfaceMaker::AT_keyword_args;
826832
}
833+
else if (_parameters.size() == first_param + 2 &&
834+
_parameters[first_param]._name == "args" &&
835+
_parameters[first_param + 1]._name == "nargs") {
836+
_flags |= F_explicit_args | F_fastcall;
837+
}
838+
else if (_parameters.size() == first_param + 3 &&
839+
_parameters[first_param]._name == "args" &&
840+
_parameters[first_param + 1]._name == "nargs" &&
841+
_parameters[first_param + 2]._name == "kwnames") {
842+
_flags |= F_explicit_args | F_fastcall;
843+
_args_type = InterfaceMaker::AT_keyword_args;
844+
}
827845
}
828846

829847
switch (_type) {
@@ -934,7 +952,8 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
934952
_args_type = InterfaceMaker::AT_single_arg;
935953

936954
} else {
937-
if (_args_type == InterfaceMaker::AT_varargs) {
955+
if (_args_type == InterfaceMaker::AT_varargs &&
956+
(_flags & F_fastcall) == 0) {
938957
// Every other method can take keyword arguments, if they take more
939958
// than one argument, and the arguments are named.
940959
for (size_t i = first_param; i < _parameters.size(); ++i) {

src/interrogate/functionRemap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class FunctionRemap {
103103
F_hash = 0x4000,
104104
F_explicit_args = 0x8000,
105105
F_explicit_cls =0x10000,
106+
F_fastcall =0x20000,
106107
};
107108

108109
typedef std::vector<Parameter> Parameters;

src/interrogate/interfaceMakerPythonNative.cxx

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4058,6 +4058,7 @@ write_function_for_name(ostream &out, Object *obj,
40584058
int max_required_args = 0;
40594059
bool all_nonconst = true;
40604060
bool has_keywords = false;
4061+
bool has_fastcall = false;
40614062

40624063
out << "/**\n * Python function wrapper for:\n";
40634064
for (ri = remaps.begin(); ri != remaps.end(); ++ri) {
@@ -4077,6 +4078,10 @@ write_function_for_name(ostream &out, Object *obj,
40774078
has_keywords = true;
40784079
}
40794080

4081+
if (remap->_flags & FunctionRemap::F_fastcall) {
4082+
has_fastcall = true;
4083+
}
4084+
40804085
max_required_args = max(max_num_args, max_required_args);
40814086

40824087
for (int i = min_num_args; i <= max_num_args; ++i) {
@@ -4169,6 +4174,58 @@ write_function_for_name(ostream &out, Object *obj,
41694174
max_required_args = collapse_default_remaps(map_sets, max_required_args);
41704175
}
41714176

4177+
if (has_fastcall) {
4178+
if (args_type == AT_keyword_args && has_keywords) {
4179+
out << " Py_ssize_t fc_nargs = PyTuple_GET_SIZE(args);\n";
4180+
out << " PyObject *const *fc_args;\n";
4181+
out << " PyObject *fc_kwnames;\n";
4182+
out << " if (kwds == nullptr) {\n";
4183+
out << " fc_args = &PyTuple_GET_ITEM(args, 0);\n";
4184+
out << " fc_kwnames = nullptr;\n";
4185+
out << " } else {\n";
4186+
out << " Py_BEGIN_CRITICAL_SECTION(kwds);\n";
4187+
out << " PyObject **fc_args_mut = (PyObject **)alloca(sizeof(PyObject *) * (fc_nargs + PyDict_GET_SIZE(kwds)));\n";
4188+
out << " for (Py_ssize_t i = 0; i < fc_nargs; i++) {\n";
4189+
out << " fc_args_mut[i] = PyTuple_GET_ITEM(args, i);\n";
4190+
out << " }\n";
4191+
out << " fc_kwnames = PyTuple_New(PyDict_GET_SIZE(kwds));\n";
4192+
out << " PyObject *key, *value;\n";
4193+
out << " Py_ssize_t pos = 0;\n";
4194+
out << " Py_ssize_t i = 0;\n";
4195+
out << " while (PyDict_Next(kwds, &pos, &key, &value)) {\n";
4196+
out << " Py_INCREF(key);\n";
4197+
out << " PyTuple_SET_ITEM(fc_kwnames, i, key);\n";
4198+
out << " fc_args_mut[fc_nargs + i] = value;\n";
4199+
out << " i++;\n";
4200+
out << " }\n";
4201+
out << " fc_args = fc_args_mut;\n";
4202+
out << " Py_END_CRITICAL_SECTION();\n";
4203+
out << " }\n";
4204+
return_flags |= RF_decref_kwnames;
4205+
}
4206+
else if (args_type == AT_varargs) {
4207+
out << " Py_ssize_t fc_nargs = PyTuple_GET_SIZE(args);\n";
4208+
out << " PyObject *const *fc_args = &PyTuple_GET_ITEM(args, 0);\n";
4209+
if (has_keywords) {
4210+
out << " PyObject *fc_kwnames = nullptr;\n";
4211+
}
4212+
}
4213+
else if (args_type == AT_single_arg) {
4214+
out << " Py_ssize_t fc_nargs = 1;\n";
4215+
out << " PyObject *const *fc_args = &arg;\n";
4216+
if (has_keywords) {
4217+
out << " PyObject *fc_kwnames = nullptr;\n";
4218+
}
4219+
}
4220+
else {
4221+
out << " Py_ssize_t fc_nargs = 0;\n";
4222+
out << " PyObject *const *fc_args = nullptr;\n";
4223+
if (has_keywords) {
4224+
out << " PyObject *fc_kwnames = nullptr;\n";
4225+
}
4226+
}
4227+
}
4228+
41724229
if (remap->_flags & FunctionRemap::F_explicit_args) {
41734230
// We have a remap that wants to handle the wrapper itself.
41744231
string expected_params;
@@ -5209,11 +5266,20 @@ write_function_instance(ostream &out, FunctionRemap *remap,
52095266
if (remap->_flags & FunctionRemap::F_explicit_args) {
52105267
// The function handles the arguments by itself.
52115268
expected_params += "*args";
5212-
pexprs.push_back("args");
5269+
if (remap->_flags & FunctionRemap::F_fastcall) {
5270+
pexprs.push_back("fc_args");
5271+
pexprs.push_back("fc_nargs");
5272+
} else {
5273+
pexprs.push_back("args");
5274+
}
52135275
if (remap->_args_type == AT_keyword_args) {
52145276
if (args_type == AT_keyword_args) {
52155277
expected_params += ", **kwargs";
5216-
pexprs.push_back("kwds");
5278+
if (remap->_flags & FunctionRemap::F_fastcall) {
5279+
pexprs.push_back("fc_kwnames");
5280+
} else {
5281+
pexprs.push_back("kwds");
5282+
}
52175283
} else {
52185284
pexprs.push_back("nullptr");
52195285
}
@@ -6726,6 +6792,11 @@ write_function_instance(ostream &out, FunctionRemap *remap,
67266792
return_flags &= ~RF_decref_args;
67276793
}
67286794

6795+
if (return_flags & RF_decref_kwnames) {
6796+
indent(out, indent_level) << "Py_XDECREF(fc_kwnames);\n";
6797+
return_flags &= ~RF_decref_kwnames;
6798+
}
6799+
67296800
// An even specialer special case for functions with void return or bool
67306801
// return. We have our own functions that do all this in a single
67316802
// function call, so it should reduce the amount of code output while not
@@ -6811,6 +6882,11 @@ write_function_instance(ostream &out, FunctionRemap *remap,
68116882
return_flags &= ~RF_decref_args;
68126883
}
68136884

6885+
if (return_flags & RF_decref_kwnames) {
6886+
indent(out, indent_level) << "Py_XDECREF(fc_kwnames);\n";
6887+
return_flags &= ~RF_decref_kwnames;
6888+
}
6889+
68146890
// Outputs code to check to see if an assertion has failed while the C++
68156891
// code was executing, and report this failure back to Python. Don't do
68166892
// this for coercion constructors since they are called by other wrapper
@@ -7006,6 +7082,9 @@ error_return(ostream &out, int indent_level, int return_flags) {
70067082
if (return_flags & RF_decref_args) {
70077083
indent(out, indent_level) << "Py_DECREF(args);\n";
70087084
}
7085+
if (return_flags & RF_decref_kwnames) {
7086+
indent(out, indent_level) << "Py_XDECREF(fc_kwnames);\n";
7087+
}
70097088

70107089
if (return_flags & RF_int) {
70117090
indent(out, indent_level) << "return -1;\n";
@@ -7032,6 +7111,9 @@ error_bad_args_return(ostream &out, int indent_level, int return_flags,
70327111
if (return_flags & RF_decref_args) {
70337112
indent(out, indent_level) << "Py_DECREF(args);\n";
70347113
}
7114+
if (return_flags & RF_decref_kwnames) {
7115+
indent(out, indent_level) << "Py_XDECREF(fc_kwnames);\n";
7116+
}
70357117

70367118
if ((return_flags & RF_err_null) != 0 &&
70377119
(return_flags & RF_pyobject) != 0) {
@@ -7083,6 +7165,10 @@ error_raise_return(ostream &out, int indent_level, int return_flags,
70837165
indent(out, indent_level) << "Py_DECREF(args);\n";
70847166
return_flags &= ~RF_decref_args;
70857167
}
7168+
if (return_flags & RF_decref_kwnames) {
7169+
indent(out, indent_level) << "Py_XDECREF(fc_kwnames);\n";
7170+
return_flags &= ~RF_decref_kwnames;
7171+
}
70867172

70877173
if (format_args.empty()) {
70887174
if (exc_type == "TypeError") {
@@ -8303,6 +8389,14 @@ is_remap_legal(FunctionRemap *remap) {
83038389
ParameterRemap *param = remap->_parameters[pn]._remap;
83048390
CPPType *orig_type = param->get_orig_type();
83058391
if (param->get_default_value() == nullptr && !is_cpp_type_legal(orig_type)) {
8392+
if ((remap->_flags & FunctionRemap::F_explicit_args) != 0 &&
8393+
(remap->_flags & FunctionRemap::F_fastcall) != 0 &&
8394+
remap->_parameters[pn]._name == "args" &&
8395+
TypeManager::is_pointer_to_pointer_to_PyObject(orig_type) &&
8396+
TypeManager::is_const_pointer_to_anything(orig_type)) {
8397+
// Allow PyObject *const *args through.
8398+
continue;
8399+
}
83068400
return false;
83078401
}
83088402
}

src/interrogate/interfaceMakerPythonNative.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ class InterfaceMakerPythonNative : public InterfaceMakerPython {
122122

123123
// Used inside a rich comparison function.
124124
RF_richcompare_zero = 0x10000,
125+
126+
// Decref temporary kwnames object.
127+
RF_decref_kwnames = 0x20000,
125128
};
126129

127130
class SlottedFunctionDef {

src/interrogate/typeManager.cxx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,26 @@ is_pair(CPPType *type) {
16211621
return false;
16221622
}
16231623

1624+
/**
1625+
* Returns true if the indicated type is PyObject *const *.
1626+
*/
1627+
bool TypeManager::
1628+
is_pointer_to_pointer_to_PyObject(CPPType *type) {
1629+
switch (type->get_subtype()) {
1630+
case CPPDeclaration::ST_const:
1631+
return is_pointer_to_pointer_to_PyObject(type->as_const_type()->_wrapped_around);
1632+
1633+
case CPPDeclaration::ST_pointer:
1634+
return is_pointer_to_PyObject(type->as_pointer_type()->_pointing_at);
1635+
1636+
case CPPDeclaration::ST_typedef:
1637+
return is_pointer_to_pointer_to_PyObject(type->as_typedef_type()->_type);
1638+
1639+
default:
1640+
return false;
1641+
}
1642+
}
1643+
16241644
/**
16251645
* Returns true if the indicated type is PyObject *.
16261646
*/

src/interrogate/typeManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class TypeManager {
105105
static bool is_pointer_to_base(CPPType *type);
106106
static bool is_const_pointer_to_base(CPPType *type);
107107
static bool is_const_ref_to_pointer_to_base(CPPType *type);
108+
static bool is_pointer_to_pointer_to_PyObject(CPPType *type);
108109
static bool is_pointer_to_PyObject(CPPType *type);
109110
static bool is_PyObject(CPPType *type);
110111
static bool is_pointer_to_PyTypeObject(CPPType *type);

0 commit comments

Comments
 (0)