diff src/if_py_both.h @ 8889:8755d57debaa v7.4.1731

commit https://github.com/vim/vim/commit/8110a091bc749d8748a20807a724a3af3ca6d509 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Apr 14 15:56:09 2016 +0200 patch 7.4.1731 Problem: Python: turns partial into simple funcref. Solution: Use partials like partials. (Nikolai Pavlov, closes https://github.com/vim/vim/issues/734)
author Christian Brabandt <cb@256bit.org>
date Thu, 14 Apr 2016 16:00:06 +0200
parents a5224eeb3546
children fe12294683d8
line wrap: on
line diff
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -72,6 +72,7 @@ typedef void (*runner)(const char *, voi
 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_
 	    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_
 FunctionConstructor(PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
 {
     PyObject	*self;
+    PyObject	*selfdictObject;
+    PyObject	*argsObject = NULL;
     char_u	*name;
-
-    if (kwargs)
-    {
-	PyErr_SET_STRING(PyExc_TypeError,
-		N_("function constructor does not accept keyword arguments"));
-	return NULL;
+    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 != 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 *subtyp
     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, PyObj
     PyObject	*selfdictObject;
     PyObject	*ret;
     int		error;
-
-    if (ConvertFromPyObject(argsObject, &args) == -1)
+    partial_T	pt;
+    partial_T	*pt_ptr = NULL;
+
+    if (ConvertFromPySequence(argsObject, &args) == -1)
 	return NULL;
 
     if (kwargs != NULL)
@@ -2940,11 +3067,17 @@ FunctionCall(FunctionObject *self, PyObj
 	}
     }
 
+    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, PyObj
     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, typv
 }
 
     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, typva
     }
     else if (PyType_IsSubtype(obj->ob_type, &FunctionType))
     {
-	if (set_string_copy(((FunctionObject *) (obj))->name, tv) == -1)
-	    return -1;
-
-	tv->v_type = VAR_FUNC;
-	func_ref(tv->vval.v_string);
+	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(func->name);
     }
     else if (PyBytes_Check(obj))
     {
@@ -6009,6 +6236,8 @@ ConvertFromPyObject(PyObject *obj, typva
     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: