Skip to content

Commit ffdaca9

Browse files
committed
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.
1 parent 148bcd3 commit ffdaca9

17 files changed

Lines changed: 267 additions & 86 deletions

src/errors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3370,4 +3370,6 @@ EXTERN char e_method_not_found_on_class_str_str[]
33703370
INIT(= N_("E1325: Method not found on class \"%s\": %s"));
33713371
EXTERN char e_member_not_found_on_object_str_str[]
33723372
INIT(= N_("E1326: Member not found on object \"%s\": %s"));
3373+
EXTERN char e_object_required_found_str[]
3374+
INIT(= N_("E1327: Object required, found %s"));
33733375
#endif

src/eval.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ eval_expr_typval(
295295

296296
// Shortcut to call a compiled function with minimal overhead.
297297
r = call_def_function(partial->pt_func, argc, argv,
298-
DEF_USE_PT_ARGV, partial, fc, rettv);
298+
DEF_USE_PT_ARGV, partial, NULL, fc, rettv);
299299
if (fc_arg == NULL)
300300
remove_funccal();
301301
if (r == FAIL)

src/proto/userfunc.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ char_u *get_scriptlocal_funcname(char_u *funcname);
4646
char_u *alloc_printable_func_name(char_u *fname);
4747
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
4848
void list_functions(regmatch_T *regmatch);
49-
ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, class_T *class_arg);
49+
ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int in_class);
5050
void ex_function(exarg_T *eap);
5151
ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
5252
void ex_defcompile(exarg_T *eap);

src/proto/vim9execute.pro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ void restore_current_ectx(ectx_T *ectx);
99
int add_defer_function(char_u *name, int argcount, typval_T *argvars);
1010
char_u *char_from_string(char_u *str, varnumber_T index);
1111
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
12-
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *loopvarinfo, ectx_T *ectx);
12+
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *lvi, ectx_T *ectx);
1313
int may_load_script(int sid, int *loaded);
1414
typval_T *lookup_debug_var(char_u *name);
1515
int may_break_in_function(ufunc_T *ufunc);
1616
int loopvars_check_refcount(loopvars_T *loopvars);
1717
int set_ref_in_loopvars(int copyID);
1818
int exe_typval_instr(typval_T *tv, typval_T *rettv);
1919
char_u *exe_substitute_instr(void);
20-
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, funccall_T *funccal, typval_T *rettv);
20+
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, object_T *object, funccall_T *funccal, typval_T *rettv);
2121
void unwind_def_callstack(ectx_T *ectx);
2222
void may_invoke_defer_funcs(ectx_T *ectx);
2323
void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);

src/proto/vim9instr.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ isn_T *generate_instr_drop(cctx_T *cctx, isntype_T isn_type, int drop);
44
isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type);
55
isn_T *generate_instr_debug(cctx_T *cctx);
66
int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
7+
int generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
78
int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
89
int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type);
910
vartype_T operator_type(type_T *type1, type_T *type2);

src/structs.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,15 +1478,16 @@ struct class_S
14781478

14791479
int class_obj_method_count;
14801480
ufunc_T **class_obj_methods; // allocated
1481-
ufunc_T *class_new_func; // new() function that was created
14821481

14831482
garray_T class_type_list; // used for type pointers
14841483
type_T class_type;
1484+
type_T class_object_type; // same as class_type but VAR_OBJECT
14851485
};
14861486

14871487
// Used for v_object of typval of VAR_OBJECT.
14881488
// The member variables follow in an array of typval_T.
1489-
struct object_S {
1489+
struct object_S
1490+
{
14901491
class_T *obj_class; // class this object is created for;
14911492
// pointer adds to class_refcount
14921493
int obj_refcount;
@@ -2123,6 +2124,7 @@ typedef struct {
21232124
int fe_evaluate; // actually evaluate expressions
21242125
partial_T *fe_partial; // for extra arguments
21252126
dict_T *fe_selfdict; // Dictionary for "self"
2127+
object_T *fe_object; // object, e.g. for "this.Func()"
21262128
typval_T *fe_basetv; // base for base->method()
21272129
type_T *fe_check_type; // type from funcref or NULL
21282130
int fe_found_var; // if the function is not found then give an

src/testdir/test_vim9_class.vim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,19 @@ def Test_class_basic()
130130
class TextPosition
131131
this.lnum: number
132132
this.col: number
133+
134+
def ToString(): string
135+
return $'({this.lnum}, {this.col})'
136+
enddef
133137
endclass
134138

135139
# use the automatically generated new() method
136140
var pos = TextPosition.new(2, 12)
137141
assert_equal(2, pos.lnum)
138142
assert_equal(12, pos.col)
143+
144+
# call an object method
145+
assert_equal('(2, 12)', pos.ToString())
139146
END
140147
v9.CheckScriptSuccess(lines)
141148
enddef

src/userfunc.c

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ get_function_line(
188188
if (theline != NULL)
189189
{
190190
if (lines_to_free->ga_len > 0
191+
&& eap->cmdlinep != NULL
191192
&& *eap->cmdlinep == ((char_u **)lines_to_free->ga_data)
192193
[lines_to_free->ga_len - 1])
193194
*eap->cmdlinep = theline;
@@ -214,7 +215,7 @@ get_function_args(
214215
garray_T *default_args,
215216
int skip,
216217
exarg_T *eap, // can be NULL
217-
class_T *class_arg,
218+
int in_class, // TRUE when inside a class
218219
garray_T *newlines, // function body lines
219220
garray_T *lines_to_free)
220221
{
@@ -294,7 +295,7 @@ get_function_args(
294295
}
295296
}
296297
}
297-
else if (class_arg != NULL && STRNCMP(p, "this.", 5) == 0)
298+
else if (in_class && STRNCMP(p, "this.", 5) == 0)
298299
{
299300
// this.memberName
300301
p += 5;
@@ -1436,7 +1437,7 @@ get_lambda_tv(
14361437
s = *arg + 1;
14371438
ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
14381439
types_optional ? &argtypes : NULL, types_optional, evalarg,
1439-
NULL, &default_args, TRUE, NULL, NULL, NULL, NULL);
1440+
NULL, &default_args, TRUE, NULL, FALSE, NULL, NULL);
14401441
if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
14411442
{
14421443
if (types_optional)
@@ -1453,7 +1454,7 @@ get_lambda_tv(
14531454
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
14541455
types_optional ? &argtypes : NULL, types_optional, evalarg,
14551456
&varargs, &default_args,
1456-
FALSE, NULL, NULL, NULL, NULL);
1457+
FALSE, NULL, FALSE, NULL, NULL);
14571458
if (ret == FAIL
14581459
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
14591460
equal_arrow || vim9script ? &white_error : NULL)) == NULL)
@@ -2733,8 +2734,8 @@ call_user_func(
27332734
profile_may_start_func(&profile_info, fp, caller);
27342735
#endif
27352736
sticky_cmdmod_flags = 0;
2736-
call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial,
2737-
fc, rettv);
2737+
call_def_function(fp, argcount, argvars, 0,
2738+
funcexe->fe_partial, funcexe->fe_object, fc, rettv);
27382739
funcdepth_decrement();
27392740
#ifdef FEAT_PROFILE
27402741
if (do_profiling == PROF_YES && (fp->uf_profiling
@@ -3957,6 +3958,7 @@ list_func_head(ufunc_T *fp, int indent)
39573958
* "is_global" is NULL.
39583959
* flags:
39593960
* TFN_INT: internal function name OK
3961+
* TFN_IN_CLASS: function in a class
39603962
* TFN_QUIET: be quiet
39613963
* TFN_NO_AUTOLOAD: do not use script autoloading
39623964
* TFN_NO_DEREF: do not dereference a Funcref
@@ -4172,8 +4174,9 @@ trans_function_name(
41724174
}
41734175

41744176
// In Vim9 script a user function is script-local by default, unless it
4175-
// starts with a lower case character: dict.func().
4176-
vim9_local = ASCII_ISUPPER(*start) && vim9script;
4177+
// starts with a lower case character: dict.func(). Or when in a class.
4178+
vim9_local = ASCII_ISUPPER(*start) && vim9script
4179+
&& (flags & TFN_IN_CLASS) == 0;
41774180

41784181
/*
41794182
* Copy the function name to allocated memory.
@@ -4211,8 +4214,10 @@ trans_function_name(
42114214
lead += (int)STRLEN(sid_buf);
42124215
}
42134216
}
4214-
else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len)
4215-
|| (vim9script && *lv.ll_name == '_')))
4217+
else if (!(flags & TFN_INT)
4218+
&& (builtin_function(lv.ll_name, len)
4219+
|| (vim9script && *lv.ll_name == '_'))
4220+
&& !((flags & TFN_IN_CLASS) && STRNCMP(lv.ll_name, "new", 3) == 0))
42164221
{
42174222
semsg(_(vim9script ? e_function_name_must_start_with_capital_str
42184223
: e_function_name_must_start_with_capital_or_s_str),
@@ -4417,15 +4422,15 @@ list_functions(regmatch_T *regmatch)
44174422
* When "name_arg" is not NULL this is a nested function, using "name_arg" for
44184423
* the function name.
44194424
* "lines_to_free" is a list of strings to be freed later.
4420-
* If "class_arg" is not NULL then the function is defined in this class.
4425+
* If "in_class" is TRUE then the function is defined inside a class.
44214426
* Returns a pointer to the function or NULL if no function defined.
44224427
*/
44234428
ufunc_T *
44244429
define_function(
44254430
exarg_T *eap,
44264431
char_u *name_arg,
44274432
garray_T *lines_to_free,
4428-
class_T *class_arg)
4433+
int in_class)
44294434
{
44304435
int j;
44314436
int c;
@@ -4500,7 +4505,7 @@ define_function(
45004505

45014506
/*
45024507
* Get the function name. There are these situations:
4503-
* func normal function name
4508+
* func normal function name, also when "in_class" is TRUE
45044509
* "name" == func, "fudi.fd_dict" == NULL
45054510
* dict.func new dictionary entry
45064511
* "name" == NULL, "fudi.fd_dict" set,
@@ -4541,7 +4546,7 @@ define_function(
45414546
}
45424547

45434548
int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC
4544-
| (class_arg == 0 ? 0 : TFN_INT);
4549+
| (in_class ? TFN_IN_CLASS : 0);
45454550
name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi);
45464551
paren = (vim_strchr(p, '(') != NULL);
45474552
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
@@ -4743,7 +4748,7 @@ define_function(
47434748
if (get_function_args(&p, ')', &newargs,
47444749
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
47454750
NULL, &varargs, &default_args, eap->skip,
4746-
eap, class_arg, &newlines, lines_to_free) == FAIL)
4751+
eap, in_class, &newlines, lines_to_free) == FAIL)
47474752
goto errret_2;
47484753
whitep = p;
47494754

@@ -4860,7 +4865,35 @@ define_function(
48604865
/*
48614866
* If there are no errors, add the function
48624867
*/
4863-
if (fudi.fd_dict == NULL)
4868+
if (fudi.fd_dict != NULL)
4869+
{
4870+
char numbuf[20];
4871+
4872+
fp = NULL;
4873+
if (fudi.fd_newkey == NULL && !eap->forceit)
4874+
{
4875+
emsg(_(e_dictionary_entry_already_exists));
4876+
goto erret;
4877+
}
4878+
if (fudi.fd_di == NULL)
4879+
{
4880+
// Can't add a function to a locked dictionary
4881+
if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
4882+
goto erret;
4883+
}
4884+
// Can't change an existing function if it is locked
4885+
else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
4886+
goto erret;
4887+
4888+
// Give the function a sequential number. Can only be used with a
4889+
// Funcref!
4890+
vim_free(name);
4891+
sprintf(numbuf, "%d", ++func_nr);
4892+
name = vim_strsave((char_u *)numbuf);
4893+
if (name == NULL)
4894+
goto erret;
4895+
}
4896+
else if (!in_class)
48644897
{
48654898
hashtab_T *ht;
48664899
char_u *find_name = name;
@@ -4967,34 +5000,6 @@ define_function(
49675000
}
49685001
}
49695002
}
4970-
else
4971-
{
4972-
char numbuf[20];
4973-
4974-
fp = NULL;
4975-
if (fudi.fd_newkey == NULL && !eap->forceit)
4976-
{
4977-
emsg(_(e_dictionary_entry_already_exists));
4978-
goto erret;
4979-
}
4980-
if (fudi.fd_di == NULL)
4981-
{
4982-
// Can't add a function to a locked dictionary
4983-
if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
4984-
goto erret;
4985-
}
4986-
// Can't change an existing function if it is locked
4987-
else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
4988-
goto erret;
4989-
4990-
// Give the function a sequential number. Can only be used with a
4991-
// Funcref!
4992-
vim_free(name);
4993-
sprintf(numbuf, "%d", ++func_nr);
4994-
name = vim_strsave((char_u *)numbuf);
4995-
if (name == NULL)
4996-
goto erret;
4997-
}
49985003

49995004
if (fp == NULL)
50005005
{
@@ -5113,7 +5118,8 @@ define_function(
51135118
hi = hash_find(&func_hashtab, name);
51145119
hi->hi_key = UF2HIKEY(fp);
51155120
}
5116-
else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL)
5121+
else if (!in_class && hash_add(&func_hashtab,
5122+
UF2HIKEY(fp), "add function") == FAIL)
51175123
{
51185124
free_fp = TRUE;
51195125
goto erret;
@@ -5198,7 +5204,7 @@ ex_function(exarg_T *eap)
51985204
garray_T lines_to_free;
51995205

52005206
ga_init2(&lines_to_free, sizeof(char_u *), 50);
5201-
(void)define_function(eap, NULL, &lines_to_free, NULL);
5207+
(void)define_function(eap, NULL, &lines_to_free, FALSE);
52025208
ga_clear_strings(&lines_to_free);
52035209
}
52045210

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ static char *(features[]) =
695695

696696
static int included_patches[] =
697697
{ /* Add new patch number below this line */
698+
/**/
699+
1041,
698700
/**/
699701
1040,
700702
/**/

src/vim.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2676,7 +2676,8 @@ typedef enum {
26762676
#define TFN_NO_DECL 0x20 // only used for GLV_NO_DECL
26772677
#define TFN_COMPILING 0x40 // only used for GLV_COMPILING
26782678
#define TFN_NEW_FUNC 0x80 // defining a new function
2679-
#define TFN_ASSIGN_WITH_OP 0x100 // only for GLV_ASSIGN_WITH_OP
2679+
#define TFN_ASSIGN_WITH_OP 0x100 // only for GLV_ASSIGN_WITH_OP
2680+
#define TFN_IN_CLASS 0x200 // function in a class
26802681

26812682
// Values for get_lval() flags argument:
26822683
#define GLV_QUIET TFN_QUIET // no error messages

0 commit comments

Comments
 (0)