diff options
author | Bram Moolenaar <Bram@vim.org> | 2016-04-14 15:56:09 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2016-04-14 15:56:09 +0200 |
commit | 8110a091bc749d8748a20807a724a3af3ca6d509 (patch) | |
tree | 87a81daf5175f9c892e6eca0e36f64a6400a1cb6 /src/if_py_both.h | |
parent | 58de0e2dcc1f2d251b74892a06d71a14973f3187 (diff) | |
download | vim-8110a091bc749d8748a20807a724a3af3ca6d509.zip |
patch 7.4.1731
Problem: Python: turns partial into simple funcref.
Solution: Use partials like partials. (Nikolai Pavlov, closes #734)
Diffstat (limited to 'src/if_py_both.h')
-rw-r--r-- | src/if_py_both.h | 294 |
1 files changed, 268 insertions, 26 deletions
diff --git a/src/if_py_both.h b/src/if_py_both.h index ee882600f..d6ae88066 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -72,6 +72,7 @@ typedef void (*runner)(const char *, void * static int ConvertFromPyObject(PyObject *, typval_T *); static int _ConvertFromPyObject(PyObject *, typval_T *, PyObject *); static int ConvertFromPyMapping(PyObject *, typval_T *); +static int ConvertFromPySequence(PyObject *, typval_T *); static PyObject *WindowNew(win_T *, tabpage_T *); static PyObject *BufferNew (buf_T *); static PyObject *LineToString(const char *); @@ -1433,6 +1434,7 @@ typedef struct pylinkedlist_S { static pylinkedlist_T *lastdict = NULL; static pylinkedlist_T *lastlist = NULL; +static pylinkedlist_T *lastfunc = NULL; static void pyll_remove(pylinkedlist_T *ref, pylinkedlist_T **last) @@ -2828,14 +2830,20 @@ typedef struct { PyObject_HEAD char_u *name; + int argc; + typval_T *argv; + dict_T *self; + pylinkedlist_T ref; } FunctionObject; static PyTypeObject FunctionType; -#define NEW_FUNCTION(name) FunctionNew(&FunctionType, name) +#define NEW_FUNCTION(name, argc, argv, self) \ + FunctionNew(&FunctionType, name, argc, argv, self) static PyObject * -FunctionNew(PyTypeObject *subtype, char_u *name) +FunctionNew(PyTypeObject *subtype, char_u *name, int argc, typval_T *argv, + dict_T *selfdict) { FunctionObject *self; @@ -2865,6 +2873,13 @@ FunctionNew(PyTypeObject *subtype, char_u *name) return NULL; } + self->argc = argc; + self->argv = argv; + self->self = selfdict; + + if (self->argv || self->self) + pyll_add((PyObject *)(self), &self->ref, &lastfunc); + return (PyObject *)(self); } @@ -2872,19 +2887,59 @@ FunctionNew(PyTypeObject *subtype, char_u *name) FunctionConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { PyObject *self; + PyObject *selfdictObject; + PyObject *argsObject = NULL; char_u *name; + typval_T selfdicttv; + typval_T argstv; + list_T *argslist = NULL; + dict_T *selfdict = NULL; + int argc = 0; + typval_T *argv = NULL; + typval_T *curtv; + listitem_T *li; - if (kwargs) + if (kwargs != NULL) { - PyErr_SET_STRING(PyExc_TypeError, - N_("function constructor does not accept keyword arguments")); - return NULL; + selfdictObject = PyDict_GetItemString(kwargs, "self"); + if (selfdictObject != NULL) + { + if (ConvertFromPyMapping(selfdictObject, &selfdicttv) == -1) + return NULL; + selfdict = selfdicttv.vval.v_dict; + } + argsObject = PyDict_GetItemString(kwargs, "args"); + if (argsObject != NULL) + { + if (ConvertFromPySequence(argsObject, &argstv) == -1) + { + dict_unref(selfdict); + return NULL; + } + argslist = argstv.vval.v_list; + + argc = argslist->lv_len; + if (argc != 0) + { + argv = PyMem_New(typval_T, (size_t) argc); + curtv = argv; + for (li = argslist->lv_first; li != NULL; li = li->li_next) + copy_tv(&li->li_tv, curtv++); + } + list_unref(argslist); + } } if (!PyArg_ParseTuple(args, "et", "ascii", &name)) + { + dict_unref(selfdict); + while (argc--) + clear_tv(&argv[argc]); + PyMem_Free(argv); return NULL; + } - self = FunctionNew(subtype, name); + self = FunctionNew(subtype, name, argc, argv, selfdict); PyMem_Free(name); @@ -2894,14 +2949,21 @@ FunctionConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) static void FunctionDestructor(FunctionObject *self) { + int i; func_unref(self->name); vim_free(self->name); + for (i = 0; i < self->argc; ++i) + clear_tv(&self->argv[i]); + PyMem_Free(self->argv); + dict_unref(self->self); + if (self->argv || self->self) + pyll_remove(&self->ref, &lastfunc); DESTRUCTOR_FINISH(self); } static char *FunctionAttrs[] = { - "softspace", + "softspace", "args", "self", NULL }; @@ -2912,6 +2974,69 @@ FunctionDir(PyObject *self) } static PyObject * +FunctionAttr(FunctionObject *self, char *name) +{ + list_T *list; + int i; + if (strcmp(name, "name") == 0) + return PyString_FromString((char *)(self->name)); + else if (strcmp(name, "args") == 0) + { + if (self->argv == NULL) + return AlwaysNone(NULL); + list = list_alloc(); + for (i = 0; i < self->argc; ++i) + list_append_tv(list, &self->argv[i]); + return NEW_LIST(list); + } + else if (strcmp(name, "self") == 0) + return self->self == NULL + ? AlwaysNone(NULL) + : NEW_DICTIONARY(self->self); + else if (strcmp(name, "__members__") == 0) + return ObjectDir(NULL, FunctionAttrs); + return NULL; +} + +/* Populate partial_T given function object. + * + * "exported" should be set to true when it is needed to construct a partial + * that may be stored in a variable (i.e. may be freed by Vim). + */ + static void +set_partial(FunctionObject *self, partial_T *pt, int exported) +{ + typval_T *curtv; + int i; + + pt->pt_name = self->name; + if (self->argv) + { + pt->pt_argc = self->argc; + if (exported) + { + pt->pt_argv = (typval_T *)alloc_clear( + sizeof(typval_T) * self->argc); + for (i = 0; i < pt->pt_argc; ++i) + copy_tv(&self->argv[i], &pt->pt_argv[i]); + } + else + pt->pt_argv = self->argv; + } + else + { + pt->pt_argc = 0; + pt->pt_argv = NULL; + } + pt->pt_dict = self->self; + if (exported && self->self) + ++pt->pt_dict->dv_refcount; + if (exported) + pt->pt_name = vim_strsave(pt->pt_name); + pt->pt_refcount = 1; +} + + static PyObject * FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs) { char_u *name = self->name; @@ -2922,8 +3047,10 @@ FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs) PyObject *selfdictObject; PyObject *ret; int error; + partial_T pt; + partial_T *pt_ptr = NULL; - if (ConvertFromPyObject(argsObject, &args) == -1) + if (ConvertFromPySequence(argsObject, &args) == -1) return NULL; if (kwargs != NULL) @@ -2940,11 +3067,17 @@ FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs) } } + if (self->argv || self->self) + { + set_partial(self, &pt, FALSE); + pt_ptr = &pt; + } + Py_BEGIN_ALLOW_THREADS Python_Lock_Vim(); VimTryStart(); - error = func_call(name, &args, NULL, selfdict, &rettv); + error = func_call(name, &args, pt_ptr, selfdict, &rettv); Python_Release_Vim(); Py_END_ALLOW_THREADS @@ -2970,14 +3103,49 @@ FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs) static PyObject * FunctionRepr(FunctionObject *self) { -#ifdef Py_TRACE_REFS - /* For unknown reason self->name may be NULL after calling - * Finalize */ - return PyString_FromFormat("<vim.Function '%s'>", - (self->name == NULL ? "<NULL>" : (char *)self->name)); -#else - return PyString_FromFormat("<vim.Function '%s'>", (char *)self->name); -#endif + PyObject *ret; + garray_T repr_ga; + int i; + char_u *tofree = NULL; + typval_T tv; + char_u numbuf[NUMBUFLEN]; + + ga_init2(&repr_ga, (int)sizeof(char), 70); + ga_concat(&repr_ga, (char_u *)"<vim.Function '"); + if (self->name) + ga_concat(&repr_ga, self->name); + else + ga_concat(&repr_ga, (char_u *)"<NULL>"); + ga_append(&repr_ga, '\''); + if (self->argv) + { + ga_concat(&repr_ga, (char_u *)", args=["); + ++emsg_silent; + for (i = 0; i < self->argc; i++) + { + if (i != 0) + ga_concat(&repr_ga, (char_u *)", "); + ga_concat(&repr_ga, tv2string(&self->argv[i], &tofree, numbuf, + get_copyID())); + vim_free(tofree); + } + --emsg_silent; + ga_append(&repr_ga, ']'); + } + if (self->self) + { + ga_concat(&repr_ga, (char_u *)", self="); + tv.v_type = VAR_DICT; + tv.vval.v_dict = self->self; + ++emsg_silent; + ga_concat(&repr_ga, tv2string(&tv, &tofree, numbuf, get_copyID())); + --emsg_silent; + vim_free(tofree); + } + ga_append(&repr_ga, '>'); + ret = PyString_FromString((char *)repr_ga.ga_data); + ga_clear(&repr_ga); + return ret; } static struct PyMethodDef FunctionMethods[] = { @@ -5551,11 +5719,13 @@ set_ref_in_py(const int copyID) pylinkedlist_T *cur; dict_T *dd; list_T *ll; + int i; int abort = FALSE; + FunctionObject *func; if (lastdict != NULL) { - for(cur = lastdict ; !abort && cur != NULL ; cur = cur->pll_prev) + for (cur = lastdict ; !abort && cur != NULL ; cur = cur->pll_prev) { dd = ((DictionaryObject *) (cur->pll_obj))->dict; if (dd->dv_copyID != copyID) @@ -5568,7 +5738,7 @@ set_ref_in_py(const int copyID) if (lastlist != NULL) { - for(cur = lastlist ; !abort && cur != NULL ; cur = cur->pll_prev) + for (cur = lastlist ; !abort && cur != NULL ; cur = cur->pll_prev) { ll = ((ListObject *) (cur->pll_obj))->list; if (ll->lv_copyID != copyID) @@ -5579,6 +5749,24 @@ set_ref_in_py(const int copyID) } } + if (lastfunc != NULL) + { + for (cur = lastfunc ; !abort && cur != NULL ; cur = cur->pll_prev) + { + func = (FunctionObject *) cur->pll_obj; + if (func->self != NULL && func->self->dv_copyID != copyID) + { + func->self->dv_copyID = copyID; + abort = abort || set_ref_in_ht( + &func->self->dv_hashtab, copyID, NULL); + } + if (func->argc) + for (i = 0; !abort && i < func->argc; ++i) + abort = abort + || set_ref_in_item(&func->argv[i], copyID, NULL, NULL); + } + } + return abort; } @@ -5880,6 +6068,34 @@ ConvertFromPyMapping(PyObject *obj, typval_T *tv) } static int +ConvertFromPySequence(PyObject *obj, typval_T *tv) +{ + PyObject *lookup_dict; + int ret; + + if (!(lookup_dict = PyDict_New())) + return -1; + + if (PyType_IsSubtype(obj->ob_type, &ListType)) + { + tv->v_type = VAR_LIST; + tv->vval.v_list = (((ListObject *)(obj))->list); + ++tv->vval.v_list->lv_refcount; + } + else if (PyIter_Check(obj) || PySequence_Check(obj)) + return convert_dl(obj, tv, pyseq_to_tv, lookup_dict); + else + { + PyErr_FORMAT(PyExc_TypeError, + N_("unable to convert %s to vim list"), + Py_TYPE_NAME(obj)); + ret = -1; + } + Py_DECREF(lookup_dict); + return ret; +} + + static int ConvertFromPyObject(PyObject *obj, typval_T *tv) { PyObject *lookup_dict; @@ -5909,11 +6125,22 @@ _ConvertFromPyObject(PyObject *obj, typval_T *tv, PyObject *lookup_dict) } else if (PyType_IsSubtype(obj->ob_type, &FunctionType)) { - if (set_string_copy(((FunctionObject *) (obj))->name, tv) == -1) - return -1; + FunctionObject *func = (FunctionObject *) obj; + if (func->self != NULL || func->argv != NULL) + { + partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); + set_partial(func, pt, TRUE); + tv->vval.v_partial = pt; + tv->v_type = VAR_PARTIAL; + } + else + { + if (set_string_copy(func->name, tv) == -1) + return -1; - tv->v_type = VAR_FUNC; - func_ref(tv->vval.v_string); + tv->v_type = VAR_FUNC; + } + func_ref(func->name); } else if (PyBytes_Check(obj)) { @@ -6009,6 +6236,8 @@ _ConvertFromPyObject(PyObject *obj, typval_T *tv, PyObject *lookup_dict) static PyObject * ConvertToPyObject(typval_T *tv) { + typval_T *argv; + int i; if (tv == NULL) { PyErr_SET_VIM(N_("internal error: NULL reference passed")); @@ -6031,10 +6260,23 @@ ConvertToPyObject(typval_T *tv) return NEW_DICTIONARY(tv->vval.v_dict); case VAR_FUNC: return NEW_FUNCTION(tv->vval.v_string == NULL - ? (char_u *)"" : tv->vval.v_string); + ? (char_u *)"" : tv->vval.v_string, + 0, NULL, NULL); case VAR_PARTIAL: + if (tv->vval.v_partial->pt_argc) + { + argv = PyMem_New(typval_T, (size_t)tv->vval.v_partial->pt_argc); + for (i = 0; i < tv->vval.v_partial->pt_argc; i++) + copy_tv(&tv->vval.v_partial->pt_argv[i], &argv[i]); + } + else + argv = NULL; + if (tv->vval.v_partial->pt_dict != NULL) + tv->vval.v_partial->pt_dict->dv_refcount++; return NEW_FUNCTION(tv->vval.v_partial == NULL - ? (char_u *)"" : tv->vval.v_partial->pt_name); + ? (char_u *)"" : tv->vval.v_partial->pt_name, + tv->vval.v_partial->pt_argc, argv, + tv->vval.v_partial->pt_dict); case VAR_UNKNOWN: case VAR_CHANNEL: case VAR_JOB: |