diff src/userfunc.c @ 9688:2ea935bdd1a1 v7.4.2120

commit https://github.com/vim/vim/commit/10ce39a0d52272a3dfff2feb8c631529f29e6740 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Jul 29 22:37:06 2016 +0200 patch 7.4.2120 Problem: User defined functions can't be a closure. Solution: Add the "closure" argument. Allow using :unlet on a bound variable. (Yasuhiro Matsumoto, Ken Takata)
author Christian Brabandt <cb@256bit.org>
date Fri, 29 Jul 2016 22:45:06 +0200
parents 8c2553beff0f
children 6226de5f8137
line wrap: on
line diff
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -59,6 +59,7 @@ struct ufunc
 #define FC_ABORT    1		/* abort function on error */
 #define FC_RANGE    2		/* function accepts range */
 #define FC_DICT	    4		/* Dict function, uses "self" */
+#define FC_CLOSURE  8		/* closure, uses outer scope variables */
 
 /* From user function to hashitem and back. */
 #define UF2HIKEY(fp) ((fp)->uf_name)
@@ -312,7 +313,7 @@ get_lambda_tv(char_u **arg, typval_T *re
 
     if (evaluate)
     {
-	int	len;
+	int	len, flags = 0;
 	char_u	*p;
 
 	sprintf((char*)name, "<lambda>%d", ++lambda_no);
@@ -341,6 +342,7 @@ get_lambda_tv(char_u **arg, typval_T *re
 	fp->uf_lines = newlines;
 	if (current_funccal != NULL && eval_lavars)
 	{
+	    flags |= FC_CLOSURE;
 	    fp->uf_scoped = current_funccal;
 	    current_funccal->fc_refcount++;
 	    if (ga_grow(&current_funccal->fc_funcs, 1) == FAIL)
@@ -361,7 +363,7 @@ get_lambda_tv(char_u **arg, typval_T *re
 	    func_do_profile(fp);
 #endif
 	fp->uf_varargs = TRUE;
-	fp->uf_flags = 0;
+	fp->uf_flags = flags;
 	fp->uf_calls = 0;
 	fp->uf_script_ID = current_SID;
 
@@ -1487,6 +1489,8 @@ list_func_head(ufunc_T *fp, int indent)
 	MSG_PUTS(" range");
     if (fp->uf_flags & FC_DICT)
 	MSG_PUTS(" dict");
+    if (fp->uf_flags & FC_CLOSURE)
+	MSG_PUTS(" closure");
     msg_clr_eos();
     if (p_verbose > 0)
 	last_set_msg(fp->uf_script_ID);
@@ -1948,7 +1952,7 @@ ex_function(exarg_T *eap)
     if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL)
 	goto errret_2;
 
-    /* find extra arguments "range", "dict" and "abort" */
+    /* find extra arguments "range", "dict", "abort" and "closure" */
     for (;;)
     {
 	p = skipwhite(p);
@@ -1967,6 +1971,11 @@ ex_function(exarg_T *eap)
 	    flags |= FC_ABORT;
 	    p += 5;
 	}
+	else if (STRNCMP(p, "closure", 7) == 0)
+	{
+	    flags |= FC_CLOSURE;
+	    p += 7;
+	}
 	else
 	    break;
     }
@@ -2299,7 +2308,25 @@ ex_function(exarg_T *eap)
     }
     fp->uf_args = newargs;
     fp->uf_lines = newlines;
-    fp->uf_scoped = NULL;
+    if ((flags & FC_CLOSURE) != 0)
+    {
+	if (current_funccal == NULL)
+	{
+	    emsg_funcname(N_("E932 Closure function should not be at top level: %s"),
+		    name);
+	    goto erret;
+	}
+	fp->uf_scoped = current_funccal;
+	current_funccal->fc_refcount++;
+	if (ga_grow(&current_funccal->fc_funcs, 1) == FAIL)
+	    goto erret;
+	((ufunc_T **)current_funccal->fc_funcs.ga_data)
+				[current_funccal->fc_funcs.ga_len++] = fp;
+	func_ref(current_funccal->func->uf_name);
+    }
+    else
+	fp->uf_scoped = NULL;
+
 #ifdef FEAT_PROFILE
     fp->uf_tml_count = NULL;
     fp->uf_tml_total = NULL;
@@ -3536,6 +3563,42 @@ get_current_funccal_dict(hashtab_T *ht)
 }
 
 /*
+ * Search hashitem in parent scope.
+ */
+    hashitem_T *
+find_hi_in_scoped_ht(char_u *name, char_u **varname, hashtab_T **pht)
+{
+    funccall_T	*old_current_funccal = current_funccal;
+    hashtab_T	*ht;
+    hashitem_T	*hi = NULL;
+
+    if (current_funccal == NULL || current_funccal->func->uf_scoped == NULL)
+      return NULL;
+
+    /* Search in parent scope which is possible to reference from lambda */
+    current_funccal = current_funccal->func->uf_scoped;
+    while (current_funccal)
+    {
+      ht = find_var_ht(name, varname);
+      if (ht != NULL && **varname != NUL)
+      {
+	  hi = hash_find(ht, *varname);
+	  if (!HASHITEM_EMPTY(hi))
+	  {
+	      *pht = ht;
+	      break;
+	  }
+      }
+      if (current_funccal == current_funccal->func->uf_scoped)
+	  break;
+      current_funccal = current_funccal->func->uf_scoped;
+    }
+    current_funccal = old_current_funccal;
+
+    return hi;
+}
+
+/*
  * Search variable in parent scope.
  */
     dictitem_T *