summaryrefslogtreecommitdiffstats
path: root/src/if_py_both.h
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-04-14 15:56:09 +0200
committerBram Moolenaar <Bram@vim.org>2016-04-14 15:56:09 +0200
commit8110a091bc749d8748a20807a724a3af3ca6d509 (patch)
tree87a81daf5175f9c892e6eca0e36f64a6400a1cb6 /src/if_py_both.h
parent58de0e2dcc1f2d251b74892a06d71a14973f3187 (diff)
patch 7.4.1731v7.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.h294
1 files changed, 268 insertions, 26 deletions
diff --git a/src/if_py_both.h b/src/if_py_both.h
index ee882600fc..d6ae880666 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: