Patch 9.0.1338
Problem: :defcompile and :disassemble can't find class method. (Ernie Rael)
Solution: Make a class name and class.method name work. (closes #11984)
Files: src/vim.h, src/eval.c, src/structs.h, src/userfunc.c,
src/proto/
userfunc.pro, src/testdir/test_vim9_class.vim
*** ../vim-9.0.1337/src/vim.h 2023-02-21 14:27:34.528360386 +0000
--- src/vim.h 2023-02-21 18:08:02.474869088 +0000
***************
*** 2722,2727 ****
--- 2722,2728 ----
#define GLV_NO_DECL TFN_NO_DECL // assignment without :var or :let
#define GLV_COMPILING TFN_COMPILING // variable may be defined later
#define GLV_ASSIGN_WITH_OP TFN_ASSIGN_WITH_OP // assignment with operator
+ #define GLV_PREFER_FUNC 0x10000 // prefer function above variable
#define DO_NOT_FREE_CNT 99999 // refcount for dict or list that should not
// be freed.
*** ../vim-9.0.1337/src/eval.c 2023-01-30 21:12:30.547422897 +0000
--- src/eval.c 2023-02-21 18:32:15.514674091 +0000
***************
*** 1529,1573 ****
if (cl != NULL)
{
lp->ll_valtype = NULL;
! int count = v_type == VAR_OBJECT ? cl->class_obj_member_count
! : cl->class_class_member_count;
! ocmember_T *members = v_type == VAR_OBJECT
! ? cl->class_obj_members
! : cl->class_class_members;
! for (int i = 0; i < count; ++i)
{
! ocmember_T *om = members + i;
! if (STRNCMP(om->ocm_name, key, p - key) == 0
! && om->ocm_name[p - key] == NUL)
{
! switch (om->ocm_access)
{
! case ACCESS_PRIVATE:
! semsg(_(e_cannot_access_private_member_str),
! om->ocm_name);
! return NULL;
! case ACCESS_READ:
! if (!(flags & GLV_READ_ONLY))
! {
! semsg(_(e_member_is_not_writable_str),
om->ocm_name);
return NULL;
! }
! break;
! case ACCESS_ALL:
! break;
! }
! lp->ll_valtype = om->ocm_type;
! if (v_type == VAR_OBJECT)
! lp->ll_tv = ((typval_T *)(
lp->ll_tv->vval.v_object + 1)) + i;
! else
! lp->ll_tv = &cl->class_members_tv[i];
! break;
}
}
if (lp->ll_valtype == NULL)
{
if (v_type == VAR_OBJECT)
--- 1529,1609 ----
if (cl != NULL)
{
lp->ll_valtype = NULL;
!
! if (flags & GLV_PREFER_FUNC)
{
! // First look for a function with this name.
! // round 1: class functions (skipped for an object)
! // round 2: object methods
! for (int round = v_type == VAR_OBJECT ? 2 : 1;
! round <= 2; ++round)
{
! int count = round == 1
! ? cl->class_class_function_count
! : cl->class_obj_method_count;
! ufunc_T **funcs = round == 1
! ? cl->class_class_functions
! : cl->class_obj_methods;
! for (int i = 0; i < count; ++i)
{
! ufunc_T *fp = funcs[i];
! char_u *ufname = (char_u *)fp->uf_name;
! if (STRNCMP(ufname, key, p - key) == 0
! && ufname[p - key] == NUL)
! {
! lp->ll_ufunc = fp;
! lp->ll_valtype = fp->uf_func_type;
! round = 3;
! break;
! }
! }
! }
! }
!
! if (lp->ll_valtype == NULL)
! {
! int count = v_type == VAR_OBJECT
! ? cl->class_obj_member_count
! : cl->class_class_member_count;
! ocmember_T *members = v_type == VAR_OBJECT
! ? cl->class_obj_members
! : cl->class_class_members;
! for (int i = 0; i < count; ++i)
! {
! ocmember_T *om = members + i;
! if (STRNCMP(om->ocm_name, key, p - key) == 0
! && om->ocm_name[p - key] == NUL)
! {
! switch (om->ocm_access)
! {
! case ACCESS_PRIVATE:
! semsg(_(e_cannot_access_private_member_str),
om->ocm_name);
return NULL;
! case ACCESS_READ:
! if ((flags & GLV_READ_ONLY) == 0)
! {
! semsg(_(e_member_is_not_writable_str),
! om->ocm_name);
! return NULL;
! }
! break;
! case ACCESS_ALL:
! break;
! }
! lp->ll_valtype = om->ocm_type;
! if (v_type == VAR_OBJECT)
! lp->ll_tv = ((typval_T *)(
lp->ll_tv->vval.v_object + 1)) + i;
! else
! lp->ll_tv = &cl->class_members_tv[i];
! break;
! }
}
}
+
if (lp->ll_valtype == NULL)
{
if (v_type == VAR_OBJECT)
*** ../vim-9.0.1337/src/structs.h 2023-02-21 12:38:46.823436717 +0000
--- src/structs.h 2023-02-21 18:28:18.821945224 +0000
***************
*** 4521,4526 ****
--- 4521,4527 ----
char_u *ll_newkey; // New key for Dict in alloc. mem or NULL.
type_T *ll_valtype; // type expected for the value or NULL
blob_T *ll_blob; // The Blob or NULL
+ ufunc_T *ll_ufunc; // The function or NULL
} lval_T;
// Structure used to save the current state. Used when executing Normal mode
*** ../vim-9.0.1337/src/userfunc.c 2023-02-21 14:27:34.528360386 +0000
--- src/userfunc.c 2023-02-21 19:29:48.830436863 +0000
***************
*** 1037,1044 ****
if (*p == '!')
p = skipwhite(p + 1);
p += eval_fname_script(p);
! vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
! NULL, NULL));
if (*skipwhite(p) == '(')
{
if (nesting == MAX_FUNC_NESTING - 1)
--- 1037,1043 ----
if (*p == '!')
p = skipwhite(p + 1);
p += eval_fname_script(p);
! vim_free(trans_function_name(&p, NULL, TRUE, 0));
if (*skipwhite(p) == '(')
{
if (nesting == MAX_FUNC_NESTING - 1)
***************
*** 4041,4050 ****
char_u **pp,
int *is_global,
int skip, // only find the end, don't evaluate
int flags,
funcdict_T *fdp, // return: info about dictionary used
partial_T **partial, // return: partial of a FuncRef
! type_T **type) // return: type of funcref if not NULL
{
char_u *name = NULL;
char_u *start;
--- 4040,4065 ----
char_u **pp,
int *is_global,
int skip, // only find the end, don't evaluate
+ int flags)
+ {
+ return trans_function_name_ext(pp, is_global, skip, flags,
+ NULL, NULL, NULL, NULL);
+ }
+
+ /*
+ * trans_function_name() with extra arguments.
+ * "fdp", "partial", "type" and "ufunc" can be NULL.
+ */
+ char_u *
+ trans_function_name_ext(
+ char_u **pp,
+ int *is_global,
+ int skip, // only find the end, don't evaluate
int flags,
funcdict_T *fdp, // return: info about dictionary used
partial_T **partial, // return: partial of a FuncRef
! type_T **type, // return: type of funcref
! ufunc_T **ufunc) // return: function
{
char_u *name = NULL;
char_u *start;
***************
*** 4079,4085 ****
start += lead;
// Note that TFN_ flags use the same values as GLV_ flags.
! end = get_lval(start, NULL, &lv, FALSE, skip, flags | GLV_READ_ONLY,
lead > 2 ? 0 : FNE_CHECK_START);
if (end == start || (vim9script && end != NULL
&& end[-1] == AUTOLOAD_CHAR && *end == '('))
--- 4094,4101 ----
start += lead;
// Note that TFN_ flags use the same values as GLV_ flags.
! end = get_lval(start, NULL, &lv, FALSE, skip,
! flags | GLV_READ_ONLY | GLV_PREFER_FUNC,
lead > 2 ? 0 : FNE_CHECK_START);
if (end == start || (vim9script && end != NULL
&& end[-1] == AUTOLOAD_CHAR && *end == '('))
***************
*** 4105,4110 ****
--- 4121,4133 ----
goto theend;
}
+ if (lv.ll_ufunc != NULL)
+ {
+ *ufunc = lv.ll_ufunc;
+ name = vim_strsave(lv.ll_ufunc->uf_name);
+ goto theend;
+ }
+
if (lv.ll_tv != NULL)
{
if (fdp != NULL)
***************
*** 4455,4462 ****
CLEAR_POINTER(fudi);
}
else
! saved = trans_function_name(&p, is_global, skip,
! flags, fudi, NULL, NULL);
*name = p;
return saved;
}
--- 4478,4485 ----
CLEAR_POINTER(fudi);
}
else
! saved = trans_function_name_ext(&p, is_global, skip,
! flags, fudi, NULL, NULL, NULL);
*name = p;
return saved;
}
***************
*** 5330,5344 ****
}
else
{
! // First try finding a method in a class, find_func_by_name() will give
! // an error if the function is not found.
ufunc = find_class_func(&arg);
if (ufunc != NULL)
return ufunc;
! fname = trans_function_name(&arg, &is_global, FALSE,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DECL,
! NULL, NULL, NULL);
}
if (fname == NULL)
{
--- 5353,5372 ----
}
else
{
! // First try finding a method in a class, trans_function_name() will
! // give an error if the function is not found.
ufunc = find_class_func(&arg);
if (ufunc != NULL)
return ufunc;
! fname = trans_function_name_ext(&arg, &is_global, FALSE,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DECL,
! NULL, NULL, NULL, &ufunc);
! if (ufunc != NULL)
! {
! vim_free(fname);
! return ufunc;
! }
}
if (fname == NULL)
{
***************
*** 5375,5387 ****
void
ex_defcompile(exarg_T *eap)
{
- ufunc_T *ufunc;
-
if (*eap->arg != NUL)
{
compiletype_T compile_type = CT_NONE;
!
! ufunc = find_func_by_name(eap->arg, &compile_type);
if (ufunc != NULL)
{
if (func_needs_compiling(ufunc, compile_type))
--- 5403,5412 ----
void
ex_defcompile(exarg_T *eap)
{
if (*eap->arg != NUL)
{
compiletype_T compile_type = CT_NONE;
! ufunc_T *ufunc = find_func_by_name(eap->arg, &compile_type);
if (ufunc != NULL)
{
if (func_needs_compiling(ufunc, compile_type))
***************
*** 5401,5407 ****
if (!HASHITEM_EMPTY(hi))
{
--todo;
! ufunc = HI2UF(hi);
if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
&& ufunc->uf_def_status == UF_TO_BE_COMPILED
&& (ufunc->uf_flags & FC_DEAD) == 0)
--- 5426,5432 ----
if (!HASHITEM_EMPTY(hi))
{
--todo;
! ufunc_T *ufunc = HI2UF(hi);
if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
&& ufunc->uf_def_status == UF_TO_BE_COMPILED
&& (ufunc->uf_flags & FC_DEAD) == 0)
***************
*** 5475,5481 ****
flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
if (no_deref)
flag |= TFN_NO_DEREF;
! p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL, NULL);
nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
--- 5500,5506 ----
flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
if (no_deref)
flag |= TFN_NO_DEREF;
! p = trans_function_name(&nm, &is_global, FALSE, flag);
nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
***************
*** 5494,5501 ****
char_u *p;
int is_global = FALSE;
! p = trans_function_name(&nm, &is_global, FALSE,
! TFN_INT|TFN_QUIET, NULL, NULL, NULL);
if (p != NULL && *nm == NUL
&& (!check || translated_function_exists(p, is_global)))
--- 5519,5525 ----
char_u *p;
int is_global = FALSE;
! p = trans_function_name(&nm, &is_global, FALSE, TFN_INT|TFN_QUIET);
if (p != NULL && *nm == NUL
&& (!check || translated_function_exists(p, is_global)))
***************
*** 5631,5638 ****
int is_global = FALSE;
p = eap->arg;
! name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi,
! NULL, NULL);
vim_free(fudi.fd_newkey);
if (name == NULL)
{
--- 5655,5662 ----
int is_global = FALSE;
p = eap->arg;
! name = trans_function_name_ext(&p, &is_global, eap->skip, 0, &fudi,
! NULL, NULL, NULL);
vim_free(fudi.fd_newkey);
if (name == NULL)
{
***************
*** 6166,6173 ****
return;
}
! tofree = trans_function_name(&arg, NULL, eap->skip, TFN_INT,
! &fudi, &partial, vim9script ? &type : NULL);
if (fudi.fd_newkey != NULL)
{
// Still need to give an error message for missing key.
--- 6190,6197 ----
return;
}
! tofree = trans_function_name_ext(&arg, NULL, eap->skip, TFN_INT,
! &fudi, &partial, vim9script ? &type : NULL, NULL);
if (fudi.fd_newkey != NULL)
{
// Still need to give an error message for missing key.
*** ../vim-9.0.1337/src/proto/
userfunc.pro 2023-02-18 14:42:40.113005575 +0000
--- src/proto/
userfunc.pro 2023-02-21 19:29:52.086440237 +0000
***************
*** 41,47 ****
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
int call_simple_func(char_u *funcname, int len, typval_T *rettv);
char_u *printable_func_name(ufunc_T *fp);
! char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type);
char_u *get_scriptlocal_funcname(char_u *funcname);
char_u *alloc_printable_func_name(char_u *fname);
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
--- 41,48 ----
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
int call_simple_func(char_u *funcname, int len, typval_T *rettv);
char_u *printable_func_name(ufunc_T *fp);
! char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags);
! char_u *trans_function_name_ext(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type, ufunc_T **ufunc);
char_u *get_scriptlocal_funcname(char_u *funcname);
char_u *alloc_printable_func_name(char_u *fname);
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
*** ../vim-9.0.1337/src/testdir/test_vim9_class.vim 2023-02-18 18:38:33.375180762 +0000
--- src/testdir/test_vim9_class.vim 2023-02-21 19:53:45.180041011 +0000
***************
*** 842,847 ****
--- 842,875 ----
v9.CheckScriptSuccess(lines)
enddef
+ def Test_class_defcompile()
+ var lines =<< trim END
+ vim9script
+
+ class C
+ def Fo(i: number): string
+ return i
+ enddef
+ endclass
+
+ defcompile C.Fo
+ END
+ v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number')
+
+ lines =<< trim END
+ vim9script
+
+ class C
+ static def Fc(): number
+ return 'x'
+ enddef
+ endclass
+
+ defcompile C.Fc
+ END
+ v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string')
+ enddef
+
def Test_class_object_to_string()
var lines =<< trim END
vim9script
*** ../vim-9.0.1337/src/version.c 2023-02-21 15:18:47.097841611 +0000
--- src/version.c 2023-02-21 19:54:16.304005842 +0000
***************
*** 697,698 ****
--- 697,700 ----
{ /* Add new patch number below this line */
+ /**/
+ 1338,
/**/
--
hundred-and-one symptoms of being an internet addict:
165. You have a web page burned into your glasses
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///