diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index e85010e80..f63f21976 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -200,7 +200,8 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict) tl_assert (cls_decl != 0); - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + bool is_tuple = PyTuple_Check (args); + int argc = args == NULL ? 0 : (is_tuple ? int (PyTuple_Size (args)) : int (PyList_Size (args))); // get number of candidates by argument count const gsi::MethodBase *meth = 0; @@ -277,9 +278,10 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict) int sc = 0; int i = 0; for (gsi::MethodBase::argument_iterator a = (*m)->begin_arguments (); is_valid && i < argc && a != (*m)->end_arguments (); ++a, ++i) { - if (test_arg (*a, PyTuple_GetItem (args, i), false /*strict*/)) { + PyObject *arg = is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i); + if (test_arg (*a, arg, false /*strict*/)) { ++sc; - } else if (test_arg (*a, PyTuple_GetItem (args, i), true /*loose*/)) { + } else if (test_arg (*a, arg, true /*loose*/)) { // non-scoring match } else { is_valid = false; @@ -326,7 +328,8 @@ match_method (int mid, PyObject *self, PyObject *args, bool strict) // one candidate, but needs checking whether compatibility is given - this avoid having to route NotImplemented over TypeError exceptions later int i = 0; for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { - if (! test_arg (*a, PyTuple_GetItem (args, i), true /*loose*/)) { + PyObject *arg = is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i); + if (! test_arg (*a, arg, true /*loose*/)) { return 0; } } @@ -607,16 +610,17 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P } } -static void +void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap) { + bool is_tuple = PyTuple_Check (args); int i = 0; - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + int argc = args == NULL ? 0 : (is_tuple ? int (PyTuple_Size (args)) : int (PyList_Size (args))); try { for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); i < argc && a != meth->end_arguments (); ++a, ++i) { - push_arg (*a, arglist, PyTuple_GetItem (args, i), heap); + push_arg (*a, arglist, is_tuple ? PyTuple_GetItem (args, i) : PyList_GetItem (args, i), heap); } } catch (tl::Exception &ex) { @@ -726,7 +730,7 @@ property_getter_adaptor (int mid, PyObject *self, PyObject *args) PYA_TRY - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + int argc = args == NULL ? 0 : (PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args))); if (argc != 0) { throw tl::Exception (tl::to_string (tr ("Property getters must not have an argument"))); } @@ -747,12 +751,12 @@ property_setter_adaptor (int mid, PyObject *self, PyObject *args) PYA_TRY - int argc = args == NULL ? 0 : int (PyTuple_Size (args)); + int argc = args == NULL ? 0 : (PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args))); if (argc != 1) { throw tl::Exception (tl::to_string (tr ("Property setter needs exactly one argument"))); } - PyObject *value = PyTuple_GetItem (args, 0); + PyObject *value = PyTuple_Check (args) ? PyTuple_GetItem (args, 0) : PyList_GetItem (args, 0); if (value) { ret = property_setter_impl (mid, self, value); } @@ -777,7 +781,8 @@ method_init_adaptor (int mid, PyObject *self, PyObject *args) p->destroy (); } - const gsi::MethodBase *meth = match_method (mid, self, args, PyTuple_Size (args) > 0 || ! p->cls_decl ()->can_default_create ()); + int argc = PyTuple_Check (args) ? int (PyTuple_Size (args)) : int (PyList_Size (args)); + const gsi::MethodBase *meth = match_method (mid, self, args, argc > 0 || ! p->cls_decl ()->can_default_create ()); if (meth && meth->smt () == gsi::MethodBase::None) { diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 0d1274d6c..d6aae3e01 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -32,6 +32,8 @@ namespace pya { +void push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args, tl::Heap &heap); + // ------------------------------------------------------------------- // Serialization adaptors for strings, variants, vectors and maps @@ -454,6 +456,8 @@ struct writer { void operator() (gsi::SerialArgs *aa, PyObject *arg, const gsi::ArgType &atype, tl::Heap *heap) { + const gsi::ClassBase *acls = atype.cls (); + if (arg == Py_None || arg == NULL) { if (! (atype.is_ptr () || atype.is_cptr ())) { @@ -465,14 +469,50 @@ struct writer } - if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) { + if (PyTuple_Check (arg) || PyList_Check (arg)) { + + // we may implicitly convert a tuple into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the list given. + + int n = PyTuple_Check (arg) ? int (PyTuple_Size (arg)) : int (PyList_Size (arg)); + const gsi::MethodBase *meth = 0; + for (gsi::ClassBase::method_iterator c = acls->begin_constructors (); c != acls->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + meth = *c; + break; + } + } + + if (!meth) { + throw tl::Exception (tl::to_string (tr ("No constructor of %s available that takes %d arguments (implicit call from tuple)")), acls->name (), n); + } + + // implicit call of constructor + gsi::SerialArgs retlist (meth->retsize ()); + gsi::SerialArgs arglist (meth->argsize ()); + + push_args (arglist, meth, arg, *heap); + + meth->call (0, arglist, retlist); + + void *new_obj = retlist.read (*heap); + if (new_obj && (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ())) { + // For pointers or refs, ownership over these objects is not transferred. + // Hence we have to keep them on the heap. + // TODO: what if the called method takes ownership using keep()? + heap->push (new gsi::ObjectHolder (acls, new_obj)); + } + + aa->write (new_obj); + + } else if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) { const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg)); if (! cls_decl) { throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name)); } - if (cls_decl->is_derived_from (atype.cls ())) { + if (cls_decl->is_derived_from (acls)) { PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); @@ -483,14 +523,15 @@ struct writer aa->write (p->obj ()); } - } else if (cls_decl->can_convert_to (atype.cls ())) { + } else if (cls_decl->can_convert_to (acls)) { PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); // We can convert objects for cref and cptr, but ownership over these objects is not transferred. // Hence we have to keep them on the heap. - void *new_obj = atype.cls ()->create_obj_from (p->cls_decl (), p->obj ()); - heap->push (new gsi::ObjectHolder (atype.cls (), new_obj)); + // TODO: what if the called method takes ownership using keep()? + void *new_obj = acls->create_obj_from (p->cls_decl (), p->obj ()); + heap->push (new gsi::ObjectHolder (acls, new_obj)); aa->write (new_obj); } else { @@ -504,7 +545,7 @@ struct writer throw tl::TypeError (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s, got %s)")), atype.cls ()->name (), Py_TYPE (arg)->tp_name)); } - if (cls_decl->is_derived_from (atype.cls ())) { + if (cls_decl->is_derived_from (acls)) { PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); @@ -515,7 +556,7 @@ struct writer aa->write (atype.cls ()->clone (p->obj ())); } - } else if (cls_decl->can_convert_to (atype.cls ())) { + } else if (cls_decl->can_convert_to (acls)) { PYAObjectBase *p = PYAObjectBase::from_pyobject (arg); aa->write (atype.cls ()->create_obj_from (cls_decl, p->obj ())); @@ -1141,19 +1182,38 @@ struct test_arg_func { void operator() (bool *ret, PyObject *arg, const gsi::ArgType &atype, bool loose) { + const gsi::ClassBase *acls = atype.cls (); + // for const X * or X *, null is an allowed value if ((atype.is_cptr () || atype.is_ptr ()) && arg == Py_None) { *ret = true; return; } + if (loose && (PyTuple_Check (arg) || PyList_Check (arg))) { + + // we may implicitly convert a tuple into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the list given. + + int n = PyTuple_Check (arg) ? int (PyTuple_Size (arg)) : int (PyList_Size (arg)); + *ret = false; + for (gsi::ClassBase::method_iterator c = acls->begin_constructors (); c != acls->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + *ret = true; + break; + } + } + return; + + } + const gsi::ClassBase *cls_decl = PythonModule::cls_for_type (Py_TYPE (arg)); if (! cls_decl) { *ret = false; return; } - if (! (cls_decl == atype.cls () || (loose && (cls_decl->is_derived_from (atype.cls ()) || cls_decl->can_convert_to(atype.cls ()))))) { + if (! (cls_decl == acls || (loose && (cls_decl->is_derived_from (atype.cls ()) || cls_decl->can_convert_to (atype.cls ()))))) { *ret = false; return; }