diff src/userfunc.c @ 31416:f088f1d97eee v9.0.1041

patch 9.0.1041: cannot define a method in a class Commit: https://github.com/vim/vim/commit/ffdaca9e6f3d39af6857ac52ced9385df203a152 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Dec 9 21:41:48 2022 +0000 patch 9.0.1041: cannot define a method in a class Problem: Cannot define a method in a class. Solution: Implement defining an object method. Make calling an object method work.
author Bram Moolenaar <Bram@vim.org>
date Fri, 09 Dec 2022 22:45:03 +0100
parents 307f68a41b03
children e31fc75f6aff
line wrap: on
line diff
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -188,6 +188,7 @@ get_function_line(
     if (theline != NULL)
     {
 	if (lines_to_free->ga_len > 0
+		&& eap->cmdlinep != NULL
 		&& *eap->cmdlinep == ((char_u **)lines_to_free->ga_data)
 						   [lines_to_free->ga_len - 1])
 	    *eap->cmdlinep = theline;
@@ -214,7 +215,7 @@ get_function_args(
     garray_T	*default_args,
     int		skip,
     exarg_T	*eap,		// can be NULL
-    class_T	*class_arg,
+    int		in_class,	// TRUE when inside a class
     garray_T	*newlines,	// function body lines
     garray_T	*lines_to_free)
 {
@@ -294,7 +295,7 @@ get_function_args(
 		}
 	    }
 	}
-	else if (class_arg != NULL && STRNCMP(p, "this.", 5) == 0)
+	else if (in_class && STRNCMP(p, "this.", 5) == 0)
 	{
 	    // this.memberName
 	    p += 5;
@@ -1436,7 +1437,7 @@ get_lambda_tv(
     s = *arg + 1;
     ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
 	    types_optional ? &argtypes : NULL, types_optional, evalarg,
-			    NULL, &default_args, TRUE, NULL, NULL, NULL, NULL);
+			   NULL, &default_args, TRUE, NULL, FALSE, NULL, NULL);
     if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
     {
 	if (types_optional)
@@ -1453,7 +1454,7 @@ get_lambda_tv(
     ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
 	    types_optional ? &argtypes : NULL, types_optional, evalarg,
 					    &varargs, &default_args,
-					    FALSE, NULL, NULL, NULL, NULL);
+					    FALSE, NULL, FALSE, NULL, NULL);
     if (ret == FAIL
 		  || (s = skip_arrow(*arg, equal_arrow, &ret_type,
 		equal_arrow || vim9script ? &white_error : NULL)) == NULL)
@@ -2733,8 +2734,8 @@ call_user_func(
 	    profile_may_start_func(&profile_info, fp, caller);
 #endif
 	sticky_cmdmod_flags = 0;
-	call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial,
-								    fc, rettv);
+	call_def_function(fp, argcount, argvars, 0,
+			   funcexe->fe_partial, funcexe->fe_object, fc, rettv);
 	funcdepth_decrement();
 #ifdef FEAT_PROFILE
 	if (do_profiling == PROF_YES && (fp->uf_profiling
@@ -3957,6 +3958,7 @@ list_func_head(ufunc_T *fp, int indent)
  * "is_global" is NULL.
  * flags:
  * TFN_INT:	    internal function name OK
+ * TFN_IN_CLASS:    function in a class
  * TFN_QUIET:	    be quiet
  * TFN_NO_AUTOLOAD: do not use script autoloading
  * TFN_NO_DEREF:    do not dereference a Funcref
@@ -4172,8 +4174,9 @@ trans_function_name(
     }
 
     // In Vim9 script a user function is script-local by default, unless it
-    // starts with a lower case character: dict.func().
-    vim9_local = ASCII_ISUPPER(*start) && vim9script;
+    // starts with a lower case character: dict.func().  Or when in a class.
+    vim9_local = ASCII_ISUPPER(*start) && vim9script
+						&& (flags & TFN_IN_CLASS) == 0;
 
     /*
      * Copy the function name to allocated memory.
@@ -4211,8 +4214,10 @@ trans_function_name(
 		lead += (int)STRLEN(sid_buf);
 	}
     }
-    else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len)
-				   || (vim9script && *lv.ll_name == '_')))
+    else if (!(flags & TFN_INT)
+	    && (builtin_function(lv.ll_name, len)
+				   || (vim9script && *lv.ll_name == '_'))
+	    && !((flags & TFN_IN_CLASS) && STRNCMP(lv.ll_name, "new", 3) == 0))
     {
 	semsg(_(vim9script ? e_function_name_must_start_with_capital_str
 			   : e_function_name_must_start_with_capital_or_s_str),
@@ -4417,7 +4422,7 @@ list_functions(regmatch_T *regmatch)
  * When "name_arg" is not NULL this is a nested function, using "name_arg" for
  * the function name.
  * "lines_to_free" is a list of strings to be freed later.
- * If "class_arg" is not NULL then the function is defined in this class.
+ * If "in_class" is TRUE then the function is defined inside a class.
  * Returns a pointer to the function or NULL if no function defined.
  */
     ufunc_T *
@@ -4425,7 +4430,7 @@ define_function(
 	exarg_T	    *eap,
 	char_u	    *name_arg,
 	garray_T    *lines_to_free,
-	class_T	    *class_arg)
+	int	    in_class)
 {
     int		j;
     int		c;
@@ -4500,7 +4505,7 @@ define_function(
 
     /*
      * Get the function name.  There are these situations:
-     * func	    normal function name
+     * func	    normal function name, also when "in_class" is TRUE
      *		    "name" == func, "fudi.fd_dict" == NULL
      * dict.func    new dictionary entry
      *		    "name" == NULL, "fudi.fd_dict" set,
@@ -4541,7 +4546,7 @@ define_function(
 	}
 
 	int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC
-					      | (class_arg == 0 ? 0 : TFN_INT);
+					       | (in_class ? TFN_IN_CLASS : 0);
 	name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi);
 	paren = (vim_strchr(p, '(') != NULL);
 	if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
@@ -4743,7 +4748,7 @@ define_function(
     if (get_function_args(&p, ')', &newargs,
 			eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
 			 NULL, &varargs, &default_args, eap->skip,
-			 eap, class_arg, &newlines, lines_to_free) == FAIL)
+			 eap, in_class, &newlines, lines_to_free) == FAIL)
 	goto errret_2;
     whitep = p;
 
@@ -4860,7 +4865,35 @@ define_function(
     /*
      * If there are no errors, add the function
      */
-    if (fudi.fd_dict == NULL)
+    if (fudi.fd_dict != NULL)
+    {
+	char	numbuf[20];
+
+	fp = NULL;
+	if (fudi.fd_newkey == NULL && !eap->forceit)
+	{
+	    emsg(_(e_dictionary_entry_already_exists));
+	    goto erret;
+	}
+	if (fudi.fd_di == NULL)
+	{
+	    // Can't add a function to a locked dictionary
+	    if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
+		goto erret;
+	}
+	    // Can't change an existing function if it is locked
+	else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
+	    goto erret;
+
+	// Give the function a sequential number.  Can only be used with a
+	// Funcref!
+	vim_free(name);
+	sprintf(numbuf, "%d", ++func_nr);
+	name = vim_strsave((char_u *)numbuf);
+	if (name == NULL)
+	    goto erret;
+    }
+    else if (!in_class)
     {
 	hashtab_T	*ht;
 	char_u		*find_name = name;
@@ -4967,34 +5000,6 @@ define_function(
 	    }
 	}
     }
-    else
-    {
-	char	numbuf[20];
-
-	fp = NULL;
-	if (fudi.fd_newkey == NULL && !eap->forceit)
-	{
-	    emsg(_(e_dictionary_entry_already_exists));
-	    goto erret;
-	}
-	if (fudi.fd_di == NULL)
-	{
-	    // Can't add a function to a locked dictionary
-	    if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
-		goto erret;
-	}
-	    // Can't change an existing function if it is locked
-	else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
-	    goto erret;
-
-	// Give the function a sequential number.  Can only be used with a
-	// Funcref!
-	vim_free(name);
-	sprintf(numbuf, "%d", ++func_nr);
-	name = vim_strsave((char_u *)numbuf);
-	if (name == NULL)
-	    goto erret;
-    }
 
     if (fp == NULL)
     {
@@ -5113,7 +5118,8 @@ define_function(
 	    hi = hash_find(&func_hashtab, name);
 	    hi->hi_key = UF2HIKEY(fp);
 	}
-	else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL)
+	else if (!in_class && hash_add(&func_hashtab,
+					 UF2HIKEY(fp), "add function") == FAIL)
 	{
 	    free_fp = TRUE;
 	    goto erret;
@@ -5198,7 +5204,7 @@ ex_function(exarg_T *eap)
     garray_T lines_to_free;
 
     ga_init2(&lines_to_free, sizeof(char_u *), 50);
-    (void)define_function(eap, NULL, &lines_to_free, NULL);
+    (void)define_function(eap, NULL, &lines_to_free, FALSE);
     ga_clear_strings(&lines_to_free);
 }