interrogate: nullptr handling, faster kwargs handling in some cases

This commit is contained in:
rdb 2017-05-14 22:18:52 +02:00
parent 652f2d7f21
commit a387fb9f35
12 changed files with 482 additions and 91 deletions

View File

@ -869,8 +869,14 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
}
if (_args_type == InterfaceMaker::AT_varargs) {
// Of course methods named "make" can still take kwargs.
_args_type = InterfaceMaker::AT_keyword_args;
// Of course methods named "make" can still take kwargs, if they are
// named.
for (int i = first_param; i < _parameters.size(); ++i) {
if (_parameters[i]._has_name) {
_args_type = InterfaceMaker::AT_keyword_args;
break;
}
}
}
} else if (fname == "operator /") {
@ -898,8 +904,13 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
} else {
if (_args_type == InterfaceMaker::AT_varargs) {
// Every other method can take keyword arguments, if they take more
// than one argument.
_args_type |= InterfaceMaker::AT_keyword_args;
// than one argument, and the arguments are named.
for (int i = first_param; i < _parameters.size(); ++i) {
if (_parameters[i]._has_name) {
_args_type |= InterfaceMaker::AT_keyword_args;
break;
}
}
}
}
break;
@ -941,8 +952,14 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
_flags |= F_coerce_constructor;
}
// Constructors always take varargs and keyword args.
_args_type = InterfaceMaker::AT_keyword_args;
// Constructors always take varargs, and possibly keyword args.
_args_type = InterfaceMaker::AT_varargs;
for (int i = first_param; i < _parameters.size(); ++i) {
if (_parameters[i]._has_name) {
_args_type = InterfaceMaker::AT_keyword_args;
break;
}
}
break;
default:

View File

@ -3389,6 +3389,7 @@ write_function_for_name(ostream &out, Object *obj,
FunctionRemap *remap = NULL;
int max_required_args = 0;
bool all_nonconst = true;
bool has_keywords = false;
out << "/**\n * Python function wrapper for:\n";
for (ri = remaps.begin(); ri != remaps.end(); ++ri) {
@ -3404,6 +3405,10 @@ write_function_for_name(ostream &out, Object *obj,
all_nonconst = false;
}
if (remap->_args_type == AT_keyword_args) {
has_keywords = true;
}
max_required_args = max(max_num_args, max_required_args);
for (int i = min_num_args; i <= max_num_args; ++i) {
@ -3450,6 +3455,19 @@ write_function_for_name(ostream &out, Object *obj,
return;
}
if (args_type == AT_keyword_args && !has_keywords) {
// We don't actually take keyword arguments. Make sure we didn't get any.
out << " if (kwds != NULL && PyDict_Size(kwds) > 0) {\n";
out << "#ifdef NDEBUG\n";
error_raise_return(out, 4, return_flags, "TypeError", "function takes no keyword arguments");
out << "#else\n";
error_raise_return(out, 4, return_flags, "TypeError",
methodNameFromCppName(remap, "", false) + "() takes no keyword arguments");
out << "#endif\n";
out << " }\n";
args_type = AT_varargs;
}
if (args_type == AT_keyword_args || args_type == AT_varargs) {
max_required_args = collapse_default_remaps(map_sets, max_required_args);
}
@ -3461,6 +3479,7 @@ write_function_for_name(ostream &out, Object *obj,
args_type, return_flags);
} else if (map_sets.size() > 1 && (args_type == AT_varargs || args_type == AT_keyword_args)) {
// We have more than one remap.
switch (args_type) {
case AT_keyword_args:
indent(out, 2) << "int parameter_count = (int)PyTuple_Size(args);\n";
@ -3498,16 +3517,50 @@ write_function_for_name(ostream &out, Object *obj,
indent(out, 2) << "case " << i << ":\n";
num_args.insert(i + add_self);
}
indent(out, 4) << "{\n";
num_args.insert(max_args + add_self);
if (min_args == 1 && max_args == 1 && args_type == AT_varargs) {
// Might as well, since we already checked the number of args.
indent(out, 6) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n";
bool strip_keyword_args = false;
// Check whether any remap actually takes keyword arguments. If not,
// then we don't have to bother checking that for every remap.
if (args_type == AT_keyword_args && max_args > 0) {
strip_keyword_args = true;
std::set<FunctionRemap *>::iterator sii;
for (sii = mii->second.begin(); sii != mii->second.end(); ++sii) {
remap = (*sii);
int first_param = remap->_has_this ? 1 : 0;
for (int i = first_param; i < remap->_parameters.size(); ++i) {
if (remap->_parameters[i]._has_name) {
strip_keyword_args = false;
break;
}
}
}
}
if (strip_keyword_args) {
// None of the remaps take any keyword arguments, so let's check that
// we take none. This saves some checks later on.
indent(out, 4) << "if (kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0) {\n";
if (min_args == 1 && min_args == 1) {
indent(out, 4) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n";
write_function_forset(out, mii->second, min_args, max_args, expected_params, 6,
coercion_allowed, true, AT_single_arg, return_flags, true, !all_nonconst);
} else {
write_function_forset(out, mii->second, min_args, max_args, expected_params, 6,
coercion_allowed, true, AT_varargs, return_flags, true, !all_nonconst);
}
} else if (min_args == 1 && max_args == 1 && args_type == AT_varargs) {
// We already checked that the args tuple has only one argument, so
// we might as well extract that from the tuple now.
indent(out, 4) << "{\n";
indent(out, 4) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n";
write_function_forset(out, mii->second, min_args, max_args, expected_params, 6,
coercion_allowed, true, AT_single_arg, return_flags, true, !all_nonconst);
} else {
indent(out, 4) << "{\n";
write_function_forset(out, mii->second, min_args, max_args, expected_params, 6,
coercion_allowed, true, args_type, return_flags, true, !all_nonconst);
}
@ -3574,14 +3627,14 @@ write_function_for_name(ostream &out, Object *obj,
if (mii->first == 0 && args_type != AT_no_args) {
switch (args_type) {
case AT_keyword_args:
out << " if (PyTuple_Size(args) > 0 || (kwds != NULL && PyDict_Size(kwds) > 0)) {\n";
out << " if (!Dtool_CheckNoArgs(args, kwds)) {\n";
out << " int parameter_count = (int)PyTuple_Size(args);\n";
out << " if (kwds != NULL) {\n";
out << " parameter_count += (int)PyDict_Size(kwds);\n";
out << " }\n";
break;
case AT_varargs:
out << " if (PyTuple_Size(args) > 0) {\n";
out << " if (!Dtool_CheckNoArgs(args)) {\n";
out << " const int parameter_count = (int)PyTuple_GET_SIZE(args);\n";
break;
case AT_single_arg:
@ -3607,14 +3660,14 @@ write_function_for_name(ostream &out, Object *obj,
} else if (args_type == AT_keyword_args && max_required_args == 1 && mii->first == 1) {
// Check this to be sure, as we handle the case of only 1 keyword arg in
// write_function_forset (not using ParseTupleAndKeywords).
out << " int parameter_count = (int)PyTuple_Size(args);\n"
" if (kwds != NULL) {\n"
" parameter_count += (int)PyDict_Size(kwds);\n"
" }\n"
out << " int parameter_count = (int)PyTuple_Size(args);\n"
" if (kwds != NULL) {\n"
" parameter_count += (int)PyDict_Size(kwds);\n"
" }\n"
" if (parameter_count != 1) {\n"
"#ifdef NDEBUG\n";
error_raise_return(out, 4, return_flags, "TypeError",
"function takes exactly 1 argument");
"function takes exactly 1 argument");
out << "#else\n";
error_raise_return(out, 4, return_flags, "TypeError",
methodNameFromCppName(remap, "", false) + "() takes exactly 1 argument (%d given)",
@ -4067,7 +4120,9 @@ int get_type_sort(CPPType *type) {
// printf(" %s\n",type->get_local_name().c_str());
// The highest numbered one will be checked first.
if (TypeManager::is_pointer_to_Py_buffer(type)) {
if (TypeManager::is_nullptr(type)) {
return 15;
} else if (TypeManager::is_pointer_to_Py_buffer(type)) {
return 14;
} else if (TypeManager::is_pointer_to_PyTypeObject(type)) {
return 13;
@ -4247,14 +4302,16 @@ write_function_forset(ostream &out,
args_type == AT_keyword_args) {
sii = remapsin.begin();
remap = (*sii);
first_param_name = remap->_parameters[(int)remap->_has_this]._name;
same_first_param = true;
if (remap->_parameters[(int)remap->_has_this]._has_name) {
first_param_name = remap->_parameters[(int)remap->_has_this]._name;
same_first_param = true;
for (++sii; sii != remapsin.end(); ++sii) {
remap = (*sii);
if (remap->_parameters[(int)remap->_has_this]._name != first_param_name) {
same_first_param = false;
break;
for (++sii; sii != remapsin.end(); ++sii) {
remap = (*sii);
if (remap->_parameters[(int)remap->_has_this]._name != first_param_name) {
same_first_param = false;
break;
}
}
}
}
@ -4263,21 +4320,9 @@ write_function_forset(ostream &out,
// Yes, they all have the same argument name (or there is only one remap).
// Extract it from the dict so we don't have to call
// ParseTupleAndKeywords.
indent(out, indent_level) << "PyObject *arg = NULL;\n";
indent(out, indent_level) << "if (PyTuple_GET_SIZE(args) == 1) {\n";
indent(out, indent_level) << " arg = PyTuple_GET_ITEM(args, 0);\n";
indent(out, indent_level) << "} else if (kwds != NULL) {\n";
indent(out, indent_level) << " arg = PyDict_GetItemString(kwds, \"" << first_param_name << "\");\n";
indent(out, indent_level) << "}\n";
if (report_errors) {
indent(out, indent_level) << "if (arg == (PyObject *)NULL) {\n";
error_raise_return(out, indent_level + 2, return_flags, "TypeError",
"Required argument '" + first_param_name + "' (pos 1) not found");
indent(out, indent_level) << "}\n";
} else {
indent(out, indent_level) << "if (arg != (PyObject *)NULL) {\n";
indent_level += 2;
}
indent(out, indent_level) << "PyObject *arg;\n";
indent(out, indent_level) << "if (Dtool_ExtractArg(&arg, args, kwds, \"" << first_param_name << "\")) {\n";
indent_level += 2;
args_type = AT_single_arg;
}
@ -4448,7 +4493,7 @@ write_function_forset(ostream &out,
}
// Close the brace we opened earlier.
if (same_first_param && !report_errors) {
if (same_first_param) {
indent_level -= 2;
indent(out, indent_level) << "}\n";
}
@ -4517,6 +4562,8 @@ write_function_instance(ostream &out, FunctionRemap *remap,
string parameter_list;
string container;
string type_check;
string param_name;
bool has_keywords = false;
vector_string pexprs;
LineStream extra_convert;
ostringstream extra_param_check;
@ -4612,7 +4659,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
CPPType *orig_type = param->get_orig_type();
CPPType *type = param->get_new_type();
CPPExpression *default_value = param->get_default_value();
string param_name = remap->get_parameter_name(pn);
param_name = remap->get_parameter_name(pn);
if (!is_cpp_type_legal(orig_type)) {
// We can't wrap this. We sometimes get here for default arguments.
@ -4667,7 +4714,14 @@ write_function_instance(ostream &out, FunctionRemap *remap,
}
string reported_name = remap->_parameters[pn]._name;
keyword_list += "\"" + reported_name + "\", ";
if (!keyword_list.empty()) {
keyword_list += ", \"" + reported_name + "\"";
} else {
keyword_list = "\"" + reported_name + "\"";
}
if (remap->_parameters[pn]._has_name) {
has_keywords = true;
}
if (param->new_type_is_atomic_string()) {
@ -4869,6 +4923,19 @@ write_function_instance(ostream &out, FunctionRemap *remap,
pexpr_string = "(PyObject_IsTrue(" + param_name + ") != 0)";
expected_params += "bool";
} else if (TypeManager::is_nullptr(type)) {
if (args_type == AT_single_arg) {
type_check = "arg == Py_None";
param_name = "arg";
} else {
indent(out, indent_level) << "PyObject *" << param_name << default_expr << ";\n";
extra_param_check << " && " << param_name << " == Py_None";
format_specifiers += "O";
parameter_list += ", &" + param_name;
}
pexpr_string = "NULL";
expected_params += "NoneType";
} else if (TypeManager::is_char(type)) {
indent(out, indent_level) << "char " << param_name << default_expr << ";\n";
@ -4907,27 +4974,43 @@ write_function_instance(ostream &out, FunctionRemap *remap,
only_pyobjects = false;
} else if (TypeManager::is_size(type)) {
// It certainly isn't the exact same thing as size_t, but Py_ssize_t
// should at least be the same size. The problem with mapping this to
// unsigned int is that that doesn't work well on 64-bit systems, on
// which size_t is a 64-bit integer.
indent(out, indent_level) << "Py_ssize_t " << param_name << default_expr << ";\n";
format_specifiers += "n";
parameter_list += ", &" + param_name;
if (args_type == AT_single_arg) {
type_check = "PyLongOrInt_Check(arg)";
extra_convert <<
"size_t arg_val = PyLongOrInt_AsSize_t(arg);\n"
"#ifndef NDEBUG\n"
"if (arg_val == (size_t)-1 && _PyErr_OCCURRED()) {\n";
error_return(extra_convert, 2, return_flags);
extra_convert <<
"}\n"
"#endif\n";
pexpr_string = "arg_val";
} else {
// It certainly isn't the exact same thing as size_t, but Py_ssize_t
// should at least be the same size. The problem with mapping this to
// unsigned int is that that doesn't work well on 64-bit systems, on
// which size_t is a 64-bit integer.
indent(out, indent_level) << "Py_ssize_t " << param_name << default_expr << ";\n";
format_specifiers += "n";
parameter_list += ", &" + param_name;
extra_convert
<< "#ifndef NDEBUG\n"
<< "if (" << param_name << " < 0) {\n";
error_raise_return(extra_convert, 2, return_flags, "OverflowError",
"can't convert negative value %zd to size_t",
param_name);
extra_convert
<< "}\n"
<< "#endif\n";
}
expected_params += "int";
only_pyobjects = false;
extra_convert
<< "#ifndef NDEBUG\n"
<< "if (" << param_name << " < 0) {\n";
error_raise_return(extra_convert, 2, return_flags, "OverflowError",
"can't convert negative value %zd to size_t",
param_name);
extra_convert
<< "}\n"
<< "#endif\n";
} else if (TypeManager::is_longlong(type)) {
// It's not trivial to do overflow checking for a long long, so we
// simply don't do it.
@ -5555,15 +5638,60 @@ write_function_instance(ostream &out, FunctionRemap *remap,
switch (args_type) {
case AT_keyword_args:
// Wrapper takes a varargs tuple and a keyword args dict.
indent(out, indent_level)
<< "static const char *keyword_list[] = {" << keyword_list << "NULL};\n";
indent(out, indent_level)
<< "if (PyArg_ParseTupleAndKeywords(args, kwds, \""
<< format_specifiers << ":" << method_name
<< "\", (char **)keyword_list" << parameter_list << ")) {\n";
if (has_keywords) {
if (only_pyobjects && max_num_args == 1) {
// But we are only expecting one object arg, which is an easy common
// case we have implemented ourselves.
if (min_num_args == 1) {
indent(out, indent_level)
<< "if (Dtool_ExtractArg(&" << param_name << ", args, kwds, " << keyword_list << ")) {\n";
} else {
indent(out, indent_level)
<< "if (Dtool_ExtractOptionalArg(&" << param_name << ", args, kwds, " << keyword_list << ")) {\n";
}
} else {
// We have to use the more expensive PyArg_ParseTupleAndKeywords.
clear_error = true;
indent(out, indent_level)
<< "static const char *keyword_list[] = {" << keyword_list << ", NULL};\n";
indent(out, indent_level)
<< "if (PyArg_ParseTupleAndKeywords(args, kwds, \""
<< format_specifiers << ":" << method_name
<< "\", (char **)keyword_list" << parameter_list << ")) {\n";
}
} else if (only_pyobjects) {
// This function actually has no named parameters, so let's not take
// any keyword arguments.
if (max_num_args == 1) {
if (min_num_args == 1) {
indent(out, indent_level)
<< "if (Dtool_ExtractArg(&" << param_name << ", args, kwds)) {\n";
} else {
indent(out, indent_level)
<< "if (Dtool_ExtractOptionalArg(&" << param_name << ", args, kwds)) {\n";
}
} else if (max_num_args == 0) {
indent(out, indent_level)
<< "if (Dtool_CheckNoArgs(args, kwds)) {\n";
} else {
clear_error = true;
indent(out, indent_level)
<< "if ((kwds == NULL || PyDict_Size(kwds) == 0) && PyArg_UnpackTuple(args, \""
<< methodNameFromCppName(remap, "", false)
<< "\", " << min_num_args << ", " << max_num_args
<< parameter_list << ")) {\n";
}
} else {
clear_error = true;
indent(out, indent_level)
<< "if ((kwds == NULL || PyDict_Size(kwds) == 0) && PyArg_ParseTuple(args, \""
<< format_specifiers << ":" << method_name
<< "\"" << parameter_list << ")) {\n";
}
++open_scopes;
clear_error = true;
indent_level += 2;
break;
@ -5572,20 +5700,28 @@ write_function_instance(ostream &out, FunctionRemap *remap,
if (only_pyobjects) {
// All parameters are PyObject*, so we can use the slightly more
// efficient PyArg_UnpackTuple function instead.
indent(out, indent_level)
<< "if (PyArg_UnpackTuple(args, \""
<< methodNameFromCppName(remap, "", false)
<< "\", " << min_num_args << ", " << max_num_args
<< parameter_list << ")) {\n";
if (min_num_args == 1 && max_num_args == 1) {
indent(out, indent_level)
<< "if (PyTuple_GET_SIZE(args) == 1) {\n";
indent(out, indent_level + 2)
<< param_name << " = PyTuple_GET_ITEM(args, 0);\n";
} else {
clear_error = true;
indent(out, indent_level)
<< "if (PyArg_UnpackTuple(args, \""
<< methodNameFromCppName(remap, "", false)
<< "\", " << min_num_args << ", " << max_num_args
<< parameter_list << ")) {\n";
}
} else {
clear_error = true;
indent(out, indent_level)
<< "if (PyArg_ParseTuple(args, \""
<< format_specifiers << ":" << method_name
<< "\"" << parameter_list << ")) {\n";
}
++open_scopes;
clear_error = true;
indent_level += 2;
break;

View File

@ -2346,6 +2346,10 @@ define_atomic_type(InterrogateType &itype, CPPSimpleType *cpptype) {
itype._atomic_token = AT_void;
break;
case CPPSimpleType::T_nullptr:
itype._atomic_token = AT_null;
break;
default:
nout << "Type \"" << *cpptype << "\" has invalid CPPSimpleType: "
<< (int)cpptype->_type << "\n";

View File

@ -364,6 +364,27 @@ is_const_ref_to_enum(CPPType *type) {
}
}
/**
* Returns true if the indicated type is nullptr_t, possibly const or a
* typedef to it.
*/
bool TypeManager::
is_nullptr(CPPType *type) {
switch (type->get_subtype()) {
case CPPDeclaration::ST_simple:
return type->as_simple_type()->_type == CPPSimpleType::T_nullptr;
case CPPDeclaration::ST_const:
return is_nullptr(type->as_const_type()->_wrapped_around);
case CPPDeclaration::ST_typedef:
return is_nullptr(type->as_typedef_type()->_type);
default:
return false;
}
}
/**
* Returns true if the indicated type is something that a scripting language
* can handle directly as a concrete, like an int or float, either const or

View File

@ -55,6 +55,7 @@ public:
static bool is_enum(CPPType *type);
static bool is_const_enum(CPPType *type);
static bool is_const_ref_to_enum(CPPType *type);
static bool is_nullptr(CPPType *type);
static bool is_simple(CPPType *type);
static bool is_const_simple(CPPType *type);
static bool is_const_ref_to_simple(CPPType *type);

View File

@ -82,7 +82,11 @@ enum AtomicToken {
// string means whatever the native string representation is.
AT_string = 7,
AT_longlong = 8
AT_longlong = 8,
// This is not a type that C has, but C++ and many scripting languages do;
// it indicates a null value, or the absence of any value.
AT_null = 9,
};
EXPCL_INTERROGATEDB void interrogate_add_search_directory(const char *dirname);

View File

@ -59,6 +59,23 @@ DTool_CreatePyInstanceTyped(T *obj, bool memory_rules) {
return DTool_CreatePyInstanceTyped((void*) obj, *known_class, memory_rules, false, obj->get_type().get_index());
}
/**
* Checks that the tuple is empty.
*/
ALWAYS_INLINE bool
Dtool_CheckNoArgs(PyObject *args) {
return PyTuple_GET_SIZE(args) == 0;
}
/**
* Checks that the tuple is empty, and that the dict is empty or NULL.
*/
ALWAYS_INLINE bool
Dtool_CheckNoArgs(PyObject *args, PyObject *kwds) {
return PyTuple_GET_SIZE(args) == 0 &&
(kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0);
}
/**
* The following functions wrap an arbitrary C++ value into a PyObject.
*/
@ -196,6 +213,11 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value) {
return PyUnicode_FromWideChar(&value, 1);
}
ALWAYS_INLINE PyObject *Dtool_WrapValue(nullptr_t) {
Py_INCREF(Py_None);
return Py_None;
}
ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value) {
return value;
}

View File

@ -31,6 +31,49 @@ static RuntimeTypeMap runtime_type_map;
static RuntimeTypeSet runtime_type_set;
static NamedTypeMap named_type_map;
#if PY_MAJOR_VERSION < 3
/**
* Given a long or int, returns a size_t, or raises an OverflowError if it is
* out of range.
*/
size_t PyLongOrInt_AsSize_t(PyObject *vv) {
if (PyInt_Check(vv)) {
long value = PyInt_AS_LONG(vv);
if (value < 0) {
PyErr_SetString(PyExc_OverflowError,
"can't convert negative value to size_t");
return (size_t)-1;
}
return (size_t)value;
}
if (!PyLong_Check(vv)) {
PyErr_BadInternalCall();
return (size_t)-1;
}
size_t bytes;
int one = 1;
int res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes,
SIZEOF_SIZE_T, (int)*(unsigned char*)&one, 0);
if (res < 0) {
return (size_t)res;
} else {
return bytes;
}
}
#endif
#if PY_VERSION_HEX < 0x03060000
INLINE static PyObject *_PyObject_CallNoArg(PyObject *func) {
PyObject *args = PyTuple_New(0);
PyObject *result = PyObject_Call(func, args, NULL);
Py_DECREF(args);
return result;
}
#endif
/**
* Given a valid (non-NULL) PyObject, does a simple check to see if it might
* be an instance of a Panda type. It does this using a signature that is
@ -767,12 +810,16 @@ int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2) {
if (func == NULL) {
PyErr_Clear();
} else {
#if PY_VERSION_HEX >= 0x03060000
PyObject *res = _PyObject_FastCall(func, &v2, 1);
#else
PyObject *res = NULL;
PyObject *args = PyTuple_Pack(1, v2);
if (args != NULL) {
res = PyObject_Call(func, args, NULL);
Py_DECREF(args);
}
#endif
Py_DECREF(func);
PyErr_Clear(); // just in case the function threw an error
// only use if the function returns an INT... hmm
@ -844,7 +891,13 @@ PyObject *DTOOL_PyObject_RichCompare(PyObject *v1, PyObject *v2, int op) {
* make_copy() method.
*/
PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) {
return PyObject_CallMethod(self, (char *)"make_copy", (char *)"()");
PyObject *callable = PyObject_GetAttrString(self, "make_copy");
if (callable == NULL) {
return NULL;
}
PyObject *result = _PyObject_CallNoArg(callable);
Py_DECREF(callable);
return result;
}
/**
@ -852,13 +905,15 @@ PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) {
* copy constructor.
*/
PyObject *copy_from_copy_constructor(PyObject *self, PyObject *noargs) {
PyObject *this_class = PyObject_Type(self);
if (this_class == NULL) {
return NULL;
}
PyObject *callable = (PyObject *)Py_TYPE(self);
PyObject *result = PyObject_CallFunction(this_class, (char *)"(O)", self);
Py_DECREF(this_class);
#if PY_VERSION_HEX >= 0x03060000
PyObject *result = _PyObject_FastCall(callable, &self, 1);
#else
PyObject *args = PyTuple_Pack(1, self);
PyObject *result = PyObject_Call(callable, args, NULL);
Py_DECREF(args);
#endif
return result;
}
@ -868,7 +923,109 @@ PyObject *copy_from_copy_constructor(PyObject *self, PyObject *noargs) {
* __copy__().
*/
PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) {
return PyObject_CallMethod(self, (char *)"__copy__", (char *)"()");
PyObject *callable = PyObject_GetAttrString(self, "__copy__");
if (callable == NULL) {
return NULL;
}
PyObject *result = _PyObject_CallNoArg(callable);
Py_DECREF(callable);
return result;
}
/**
* A more efficient version of PyArg_ParseTupleAndKeywords for the special
* case where there is only a single PyObject argument.
*/
bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds,
const char *keyword) {
if (PyTuple_GET_SIZE(args) == 1) {
if (kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0) {
*result = PyTuple_GET_ITEM(args, 0);
return true;
}
} else if (PyTuple_GET_SIZE(args) == 0) {
PyObject *key;
Py_ssize_t ppos = 0;
if (kwds != NULL && ((PyDictObject *)kwds)->ma_used == 1 &&
PyDict_Next(kwds, &ppos, &key, result)) {
// We got the item, we just need to make sure that it had the right key.
#if PY_VERSION_HEX >= 0x03050000
return PyUnicode_CheckExact(key) && _PyUnicode_EqualToASCIIString(key, keyword);
#elif PY_MAJOR_VERSION >= 3
return PyUnicode_CheckExact(key) && PyUnicode_CompareWithASCIIString(key, keyword) == 0;
#else
return PyString_CheckExact(key) && strcmp(PyString_AS_STRING(key), keyword) == 0;
#endif
}
}
return false;
}
/**
* Variant of Dtool_ExtractArg that does not accept a keyword argument.
*/
bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds) {
if (PyTuple_GET_SIZE(args) == 1 &&
(kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0)) {
*result = PyTuple_GET_ITEM(args, 0);
return true;
}
return false;
}
/**
* A more efficient version of PyArg_ParseTupleAndKeywords for the special
* case where there is only a single optional PyObject argument.
*
* Returns true if valid (including if there were 0 items), false if there was
* an error, such as an invalid number of parameters.
*/
bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds,
const char *keyword) {
if (PyTuple_GET_SIZE(args) == 1) {
if (kwds == NULL || ((PyDictObject *)kwds)->ma_used == 0) {
*result = PyTuple_GET_ITEM(args, 0);
return true;
}
} else if (PyTuple_GET_SIZE(args) == 0) {
if (kwds != NULL && ((PyDictObject *)kwds)->ma_used == 1) {
PyObject *key;
Py_ssize_t ppos = 0;
if (!PyDict_Next(kwds, &ppos, &key, result)) {
return true;
}
// We got the item, we just need to make sure that it had the right key.
#if PY_VERSION_HEX >= 0x03050000
return PyUnicode_CheckExact(key) && _PyUnicode_EqualToASCIIString(key, keyword);
#elif PY_MAJOR_VERSION >= 3
return PyUnicode_CheckExact(key) && PyUnicode_CompareWithASCIIString(key, keyword) == 0;
#else
return PyString_CheckExact(key) && strcmp(PyString_AS_STRING(key), keyword) == 0;
#endif
} else {
return true;
}
}
return false;
}
/**
* Variant of Dtool_ExtractOptionalArg that does not accept a keyword argument.
*/
bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds) {
if (kwds != NULL && ((PyDictObject *)kwds)->ma_used != 0) {
return false;
}
if (PyTuple_GET_SIZE(args) == 1) {
*result = PyTuple_GET_ITEM(args, 0);
return true;
}
return (PyTuple_GET_SIZE(args) == 0);
}
/**

View File

@ -107,11 +107,14 @@ inline PyObject* doPy_RETURN_FALSE()
#define PyInt_Check PyLong_Check
#define PyInt_AsLong PyLong_AsLong
#define PyInt_AS_LONG PyLong_AS_LONG
#define PyLongOrInt_AsSize_t PyLong_AsSize_t
#else
#define PyLongOrInt_Check(x) (PyInt_Check(x) || PyLong_Check(x))
// PyInt_FromSize_t automatically picks the right type.
#define PyLongOrInt_AS_LONG PyInt_AsLong
EXPCL_INTERROGATEDB size_t PyLongOrInt_AsSize_t(PyObject *);
// For more portably defining hash functions.
typedef long Py_hash_t;
#endif
@ -469,6 +472,21 @@ struct Dtool_SequenceWrapper {
EXPCL_INTERROGATEDB extern PyTypeObject Dtool_SequenceWrapper_Type;
/**
* These functions check whether the arguments passed to a function conform to
* certain expectations.
*/
ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args);
ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args, PyObject *kwds);
EXPCL_INTERROGATEDB bool Dtool_ExtractArg(PyObject **result, PyObject *args,
PyObject *kwds, const char *keyword);
EXPCL_INTERROGATEDB bool Dtool_ExtractArg(PyObject **result, PyObject *args,
PyObject *kwds);
EXPCL_INTERROGATEDB bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
PyObject *kwds, const char *keyword);
EXPCL_INTERROGATEDB bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
PyObject *kwds);
/**
* These functions convert a C++ value into the corresponding Python object.
* This used to be generated by the code generator, but it seems more reliable
@ -491,6 +509,7 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::string *value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::wstring *value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(char value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(nullptr_t);
ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char> &value);

View File

@ -23,7 +23,5 @@
#define offsetof(type,member) ((size_t) &(((type*)0)->member))
typedef decltype(nullptr) nullptr_t;
#endif

View File

@ -41,11 +41,7 @@ inline namespace std {
struct timeval;
#ifdef __cplusplus
#define NULL 0L
#else
#define NULL ((void *)0)
#endif
typedef decltype(nullptr) nullptr_t;
// One day, we might extend interrogate to be able to parse this,
// but we currently don't need it.

View File

@ -32,6 +32,7 @@ extern "C" {
EXPCL_PYSTUB int PyDict_GetItem(...);
EXPCL_PYSTUB int PyDict_GetItemString(...);
EXPCL_PYSTUB int PyDict_New(...);
EXPCL_PYSTUB int PyDict_Next(...);
EXPCL_PYSTUB int PyDict_SetItem(...);
EXPCL_PYSTUB int PyDict_SetItemString(...);
EXPCL_PYSTUB int PyDict_Size(...);
@ -49,7 +50,6 @@ extern "C" {
EXPCL_PYSTUB int PyErr_WarnEx(...);
EXPCL_PYSTUB int PyEval_CallFunction(...);
EXPCL_PYSTUB int PyEval_CallObjectWithKeywords(...);
EXPCL_PYSTUB int PyEval_InitThreads(...);
EXPCL_PYSTUB int PyEval_RestoreThread(...);
EXPCL_PYSTUB int PyEval_SaveThread(...);
EXPCL_PYSTUB int PyFloat_AsDouble(...);
@ -163,6 +163,7 @@ extern "C" {
EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...);
EXPCL_PYSTUB int PyUnicode_AsWideChar(...);
EXPCL_PYSTUB int PyUnicode_AsWideCharString(...);
EXPCL_PYSTUB int PyUnicode_CompareWithASCIIString(...);
EXPCL_PYSTUB int PyUnicode_FromFormat(...);
EXPCL_PYSTUB int PyUnicode_FromString(...);
EXPCL_PYSTUB int PyUnicode_FromStringAndSize(...);
@ -180,12 +181,16 @@ extern "C" {
EXPCL_PYSTUB int _PyArg_ParseTuple_SizeT(...);
EXPCL_PYSTUB int _PyArg_ParseTupleAndKeywords_SizeT(...);
EXPCL_PYSTUB int _PyArg_Parse_SizeT(...);
EXPCL_PYSTUB int _PyErr_BadInternalCall(...);
EXPCL_PYSTUB int _PyLong_AsByteArray(...);
EXPCL_PYSTUB int _PyObject_CallFunction_SizeT(...);
EXPCL_PYSTUB int _PyObject_CallMethod_SizeT(...);
EXPCL_PYSTUB int _PyObject_DebugFree(...);
EXPCL_PYSTUB int _PyObject_Del(...);
EXPCL_PYSTUB int _PyObject_FastCallDict(...);
EXPCL_PYSTUB int _PyUnicode_AsString(...);
EXPCL_PYSTUB int _PyUnicode_AsStringAndSize(...);
EXPCL_PYSTUB int _PyUnicode_EqualToASCIIString(...);
EXPCL_PYSTUB int _Py_AddToAllObjects(...);
EXPCL_PYSTUB int _Py_BuildValue_SizeT(...);
EXPCL_PYSTUB int _Py_Dealloc(...);
@ -198,6 +203,7 @@ extern "C" {
EXPCL_PYSTUB void Py_Initialize();
EXPCL_PYSTUB int Py_IsInitialized();
EXPCL_PYSTUB void PyEval_InitThreads();
EXPCL_PYSTUB extern void *PyExc_AssertionError;
EXPCL_PYSTUB extern void *PyExc_AttributeError;
@ -208,6 +214,7 @@ extern "C" {
EXPCL_PYSTUB extern void *PyExc_ImportError;
EXPCL_PYSTUB extern void *PyExc_IndexError;
EXPCL_PYSTUB extern void *PyExc_OSError;
EXPCL_PYSTUB extern void *PyExc_OverflowError;
EXPCL_PYSTUB extern void *PyExc_RuntimeError;
EXPCL_PYSTUB extern void *PyExc_StandardError;
EXPCL_PYSTUB extern void *PyExc_StopIteration;
@ -241,6 +248,7 @@ int PyDict_DelItemString(...) { return 0; }
int PyDict_GetItem(...) { return 0; }
int PyDict_GetItemString(...) { return 0; }
int PyDict_New(...) { return 0; };
int PyDict_Next(...) { return 0; };
int PyDict_SetItem(...) { return 0; };
int PyDict_SetItemString(...) { return 0; };
int PyDict_Size(...){ return 0; }
@ -372,6 +380,7 @@ int PyUnicode_AsUTF8(...) { return 0; }
int PyUnicode_AsUTF8AndSize(...) { return 0; }
int PyUnicode_AsWideChar(...) { return 0; }
int PyUnicode_AsWideCharString(...) { return 0; }
int PyUnicode_CompareWithASCIIString(...) { return 0; }
int PyUnicode_FromFormat(...) { return 0; }
int PyUnicode_FromString(...) { return 0; }
int PyUnicode_FromStringAndSize(...) { return 0; }
@ -389,12 +398,16 @@ int Py_InitModule4TraceRefs_64(...) { return 0; };
int _PyArg_ParseTuple_SizeT(...) { return 0; };
int _PyArg_ParseTupleAndKeywords_SizeT(...) { return 0; };
int _PyArg_Parse_SizeT(...) { return 0; };
int _PyErr_BadInternalCall(...) { return 0; };
int _PyLong_AsByteArray(...) { return 0; };
int _PyObject_CallFunction_SizeT(...) { return 0; };
int _PyObject_CallMethod_SizeT(...) { return 0; };
int _PyObject_DebugFree(...) { return 0; };
int _PyObject_Del(...) { return 0; };
int _PyObject_FastCallDict(...) { return 0; };
int _PyUnicode_AsString(...) { return 0; };
int _PyUnicode_AsStringAndSize(...) { return 0; };
int _PyUnicode_EqualToASCIIString(...) { return 0; };
int _Py_AddToAllObjects(...) { return 0; };
int _Py_BuildValue_SizeT(...) { return 0; };
int _Py_Dealloc(...) { return 0; };
@ -411,6 +424,8 @@ void Py_Initialize() {
int Py_IsInitialized() {
return 0;
}
void PyEval_InitThreads() {
}
void *PyExc_AssertionError = (void *)NULL;
@ -422,6 +437,7 @@ void *PyExc_FutureWarning = (void *)NULL;
void *PyExc_ImportError = (void *)NULL;
void *PyExc_IndexError = (void *)NULL;
void *PyExc_OSError = (void *)NULL;
void *PyExc_OverflowError = (void *)NULL;
void *PyExc_RuntimeError = (void *)NULL;
void *PyExc_StandardError = (void *)NULL;
void *PyExc_StopIteration = (void *)NULL;