mirror of
				https://github.com/vim/vim.git
				synced 2025-10-30 09:47:20 -04:00 
			
		
		
		
	patch 8.1.1820: using expr->FuncRef() does not work
Problem: Using expr->FuncRef() does not work. Solution: Make FuncRef work as a method.
This commit is contained in:
		
							
								
								
									
										167
									
								
								src/eval.c
									
									
									
									
									
								
							
							
						
						
									
										167
									
								
								src/eval.c
									
									
									
									
									
								
							| @@ -3654,6 +3654,74 @@ pattern_match(char_u *pat, char_u *text, int ic) | ||||
|     return matches; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Handle a name followed by "(".  Both for just "name(arg)" and for | ||||
|  * "expr->name(arg)". | ||||
|  * Returns OK or FAIL. | ||||
|  */ | ||||
|     static int | ||||
| eval_func( | ||||
| 	char_u	    **arg,	// points to "(", will be advanced | ||||
| 	char_u	    *name, | ||||
| 	int	    name_len, | ||||
| 	typval_T    *rettv, | ||||
| 	int	    evaluate, | ||||
| 	typval_T    *basetv)	// "expr" for "expr->name(arg)" | ||||
| { | ||||
|     char_u	*s = name; | ||||
|     int		len = name_len; | ||||
|     partial_T	*partial; | ||||
|     int		ret = OK; | ||||
|  | ||||
|     if (!evaluate) | ||||
| 	check_vars(s, len); | ||||
|  | ||||
|     /* If "s" is the name of a variable of type VAR_FUNC | ||||
|      * use its contents. */ | ||||
|     s = deref_func_name(s, &len, &partial, !evaluate); | ||||
|  | ||||
|     /* Need to make a copy, in case evaluating the arguments makes | ||||
|      * the name invalid. */ | ||||
|     s = vim_strsave(s); | ||||
|     if (s == NULL) | ||||
| 	ret = FAIL; | ||||
|     else | ||||
|     { | ||||
| 	funcexe_T funcexe; | ||||
|  | ||||
| 	// Invoke the function. | ||||
| 	vim_memset(&funcexe, 0, sizeof(funcexe)); | ||||
| 	funcexe.firstline = curwin->w_cursor.lnum; | ||||
| 	funcexe.lastline = curwin->w_cursor.lnum; | ||||
| 	funcexe.doesrange = &len; | ||||
| 	funcexe.evaluate = evaluate; | ||||
| 	funcexe.partial = partial; | ||||
| 	funcexe.basetv = basetv; | ||||
| 	ret = get_func_tv(s, len, rettv, arg, &funcexe); | ||||
|     } | ||||
|     vim_free(s); | ||||
|  | ||||
|     /* If evaluate is FALSE rettv->v_type was not set in | ||||
|      * get_func_tv, but it's needed in handle_subscript() to parse | ||||
|      * what follows. So set it here. */ | ||||
|     if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') | ||||
|     { | ||||
| 	rettv->vval.v_string = NULL; | ||||
| 	rettv->v_type = VAR_FUNC; | ||||
|     } | ||||
|  | ||||
|     /* Stop the expression evaluation when immediately | ||||
|      * aborting on error, or when an interrupt occurred or | ||||
|      * an exception was thrown but not caught. */ | ||||
|     if (evaluate && aborting()) | ||||
|     { | ||||
| 	if (ret == OK) | ||||
| 	    clear_tv(rettv); | ||||
| 	ret = FAIL; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * The "evaluate" argument: When FALSE, the argument is only parsed but not | ||||
|  * executed.  The function may return OK, but the rettv will be of type | ||||
| @@ -4671,55 +4739,7 @@ eval7( | ||||
| 	else | ||||
| 	{ | ||||
| 	    if (**arg == '(')		/* recursive! */ | ||||
| 	    { | ||||
| 		partial_T *partial; | ||||
|  | ||||
| 		if (!evaluate) | ||||
| 		    check_vars(s, len); | ||||
|  | ||||
| 		/* If "s" is the name of a variable of type VAR_FUNC | ||||
| 		 * use its contents. */ | ||||
| 		s = deref_func_name(s, &len, &partial, !evaluate); | ||||
|  | ||||
| 		/* Need to make a copy, in case evaluating the arguments makes | ||||
| 		 * the name invalid. */ | ||||
| 		s = vim_strsave(s); | ||||
| 		if (s == NULL) | ||||
| 		    ret = FAIL; | ||||
| 		else | ||||
| 		{ | ||||
| 		    funcexe_T funcexe; | ||||
|  | ||||
| 		    // Invoke the function. | ||||
| 		    vim_memset(&funcexe, 0, sizeof(funcexe)); | ||||
| 		    funcexe.firstline = curwin->w_cursor.lnum; | ||||
| 		    funcexe.lastline = curwin->w_cursor.lnum; | ||||
| 		    funcexe.doesrange = &len; | ||||
| 		    funcexe.evaluate = evaluate; | ||||
| 		    funcexe.partial = partial; | ||||
| 		    ret = get_func_tv(s, len, rettv, arg, &funcexe); | ||||
| 		} | ||||
| 		vim_free(s); | ||||
|  | ||||
| 		/* If evaluate is FALSE rettv->v_type was not set in | ||||
| 		 * get_func_tv, but it's needed in handle_subscript() to parse | ||||
| 		 * what follows. So set it here. */ | ||||
| 		if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') | ||||
| 		{ | ||||
| 		    rettv->vval.v_string = NULL; | ||||
| 		    rettv->v_type = VAR_FUNC; | ||||
| 		} | ||||
|  | ||||
| 		/* Stop the expression evaluation when immediately | ||||
| 		 * aborting on error, or when an interrupt occurred or | ||||
| 		 * an exception was thrown but not caught. */ | ||||
| 		if (evaluate && aborting()) | ||||
| 		{ | ||||
| 		    if (ret == OK) | ||||
| 			clear_tv(rettv); | ||||
| 		    ret = FAIL; | ||||
| 		} | ||||
| 	    } | ||||
| 		ret = eval_func(arg, s, len, rettv, evaluate, NULL); | ||||
| 	    else if (evaluate) | ||||
| 		ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); | ||||
| 	    else | ||||
| @@ -4815,55 +4835,42 @@ eval_method( | ||||
| { | ||||
|     char_u	*name; | ||||
|     long	len; | ||||
|     funcexe_T	funcexe; | ||||
|     int		ret = OK; | ||||
|     char_u	*alias; | ||||
|     typval_T	base = *rettv; | ||||
|     int		ret; | ||||
|  | ||||
|     // Skip over the ->. | ||||
|     *arg += 2; | ||||
|     rettv->v_type = VAR_UNKNOWN; | ||||
|  | ||||
|     // Locate the method name. | ||||
|     name = *arg; | ||||
|     for (len = 0; eval_isnamec(name[len]); ++len) | ||||
| 	; | ||||
|     if (len == 0) | ||||
|     len = get_name_len(arg, &alias, evaluate, TRUE); | ||||
|     if (alias != NULL) | ||||
| 	name = alias; | ||||
|  | ||||
|     if (len <= 0) | ||||
|     { | ||||
| 	if (verbose) | ||||
| 	    emsg(_("E260: Missing name after ->")); | ||||
| 	return FAIL; | ||||
| 	ret = FAIL; | ||||
|     } | ||||
|  | ||||
|     // Check for the "(".  Skip over white space after it. | ||||
|     if (name[len] != '(') | ||||
|     else | ||||
|     { | ||||
| 	if (verbose) | ||||
| 	    semsg(_(e_missingparen), name); | ||||
| 	return FAIL; | ||||
| 	if (**arg != '(') | ||||
| 	{ | ||||
| 	    if (verbose) | ||||
| 		semsg(_(e_missingparen), name); | ||||
| 	    ret = FAIL; | ||||
| 	} | ||||
| 	else | ||||
| 	    ret = eval_func(arg, name, len, rettv, evaluate, &base); | ||||
|     } | ||||
|     *arg += len; | ||||
|  | ||||
|     // TODO: if "name" is a function reference, resolve it. | ||||
|  | ||||
|     vim_memset(&funcexe, 0, sizeof(funcexe)); | ||||
|     funcexe.evaluate = evaluate; | ||||
|     funcexe.basetv = &base; | ||||
|     rettv->v_type = VAR_UNKNOWN; | ||||
|     ret = get_func_tv(name, len, rettv, arg, &funcexe); | ||||
|  | ||||
|     /* Clear the funcref afterwards, so that deleting it while | ||||
|      * evaluating the arguments is possible (see test55). */ | ||||
|     if (evaluate) | ||||
| 	clear_tv(&base); | ||||
|  | ||||
|     /* Stop the expression evaluation when immediately aborting on | ||||
|      * error, or when an interrupt occurred or an exception was thrown | ||||
|      * but not caught. */ | ||||
|     if (aborting()) | ||||
|     { | ||||
| 	if (ret == OK) | ||||
| 	    clear_tv(rettv); | ||||
| 	ret = FAIL; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| " Tests for ->method() | ||||
|  | ||||
| func Test_list() | ||||
| func Test_list_method() | ||||
|   let l = [1, 2, 3] | ||||
|   call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) | ||||
|   eval l->assert_equal(l) | ||||
| @@ -34,7 +34,7 @@ func Test_list() | ||||
|   call assert_fails('eval l->values()', 'E715:') | ||||
| endfunc | ||||
|  | ||||
| func Test_dict() | ||||
| func Test_dict_method() | ||||
|   let d = #{one: 1, two: 2, three: 3} | ||||
|  | ||||
|   call assert_equal(d, d->copy()) | ||||
| @@ -66,7 +66,7 @@ func Test_dict() | ||||
|   call assert_equal([1, 2, 3], d->values()) | ||||
| endfunc | ||||
|  | ||||
| func Test_string() | ||||
| func Test_string_method() | ||||
|   call assert_equal(['1', '2', '3'], '1 2 3'->split()) | ||||
|   call assert_equal([1, 2, 3], '1 2 3'->split()->map({i, v -> str2nr(v)})) | ||||
|   call assert_equal([65, 66, 67], 'ABC'->str2list()) | ||||
| @@ -76,7 +76,7 @@ func Test_string() | ||||
|   call assert_equal('axc', 'abc'->substitute('b', 'x', '')) | ||||
| endfunc | ||||
|  | ||||
| func Test_append() | ||||
| func Test_method_append() | ||||
|   new | ||||
|   eval ['one', 'two', 'three']->append(1) | ||||
|   call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) | ||||
| @@ -89,3 +89,16 @@ func Test_append() | ||||
|  | ||||
|   exe 'bwipe! ' .. bnr | ||||
| endfunc | ||||
|  | ||||
| func Test_method_funcref() | ||||
|   func Concat(one, two, three) | ||||
|     return a:one .. a:two .. a:three | ||||
|   endfunc | ||||
|   let FuncRef = function('Concat') | ||||
|   eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail') | ||||
|  | ||||
|   let Partial = function('Concat', ['two']) | ||||
|   eval 'one'->Partial('three')->assert_equal('onetwothree') | ||||
|  | ||||
|   delfunc Concat | ||||
| endfunc | ||||
|   | ||||
| @@ -1498,6 +1498,7 @@ call_func( | ||||
|     typval_T	argv[MAX_FUNC_ARGS + 1]; // used when "partial" or | ||||
| 					 // "funcexe->basetv" is not NULL | ||||
|     int		argv_clear = 0; | ||||
|     int		argv_base = 0; | ||||
|     partial_T	*partial = funcexe->partial; | ||||
|  | ||||
|     // Make a copy of the name, if it comes from a funcref variable it could | ||||
| @@ -1590,9 +1591,9 @@ call_func( | ||||
| 		    mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount); | ||||
| 		    argv[0] = *funcexe->basetv; | ||||
| 		    argcount++; | ||||
| 		    argvars = argv; | ||||
| 		    argv_base = 1; | ||||
| 		} | ||||
| 		else | ||||
| 		    memcpy(argv, argvars, sizeof(typval_T) * argcount); | ||||
|  | ||||
| 		if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) | ||||
| 		    *funcexe->doesrange = TRUE; | ||||
| @@ -1621,7 +1622,7 @@ call_func( | ||||
| 			did_save_redo = TRUE; | ||||
| 		    } | ||||
| 		    ++fp->uf_calls; | ||||
| 		    call_user_func(fp, argcount, argv, rettv, | ||||
| 		    call_user_func(fp, argcount, argvars, rettv, | ||||
| 					 funcexe->firstline, funcexe->lastline, | ||||
| 				  (fp->uf_flags & FC_DICT) ? selfdict : NULL); | ||||
| 		    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) | ||||
| @@ -1698,8 +1699,10 @@ call_func( | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     // clear the copies made from the partial | ||||
|     while (argv_clear > 0) | ||||
| 	clear_tv(&argv[--argv_clear]); | ||||
| 	clear_tv(&argv[--argv_clear + argv_base]); | ||||
|  | ||||
|     vim_free(tofree); | ||||
|     vim_free(name); | ||||
|  | ||||
|   | ||||
| @@ -773,6 +773,8 @@ static char *(features[]) = | ||||
|  | ||||
| static int included_patches[] = | ||||
| {   /* Add new patch number below this line */ | ||||
| /**/ | ||||
|     1820, | ||||
| /**/ | ||||
|     1819, | ||||
| /**/ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user